输入函数

read

  1. 达到nbyte上限
  2. 遇到/n

输出函数

puts

输出字符串,直到空字符,但不包括空字符(包括换行符)

有时可用于泄露地址

printf

printf(‘%s’)输出字符串,而字符串以末尾的\0为标志,所以printf(‘%s’)输出字符直到遇到*\0*

Others

strcpy

字符串拷贝,如何来界定一个字符串呢?’\0’

有时可用于覆盖特定数据

例如:2016 BCTF bcloud

因为又在这里栽跟头了所以写一篇博客来记录一下orz

题目通过printf泄露地址的情况

例如:

1
2
3
4
5
6
#include<stdio.h>
int main(){
int a;
printf("%p",&a);
return 0;
}

0

可以看到输出的数据是0xe也就是14个字节

我们都知道一个64位程序的地址最多也就8个字节,为什么这个地址是14个字节呢?

原因是printf在输出的时候是将这个6个字节的地址转换成的字符串再加上0x进行输出的

知道原因过后我们的目标就很明确了:将这个14个字节的字符串转换为6个字节的数据

首先是要对获取到的数据进行处理:

我们需要用到python中的decode()方法将字节数据转换为字符串再使用lstrip('0x')去掉字符串左边的‘0x’

再使用bytes.fromhex()方法将十六进制字符串转换为字节数据

但是还没完,我们知道在大部分机器上数据是以小端序的顺序储存的,也就是我们要把我们得到的字节再进行反序操作

即使用[::-1]

完整操作如下:

1
2
3
4
raw_bytes = r(14)#接收数据
hex_str = raw_bytes.decode().lstrip('0x')#.rstrip('\x00') 如果结尾有多的'\x00'还需要.rstrip('\x00')来进行去除
hex_data = bytes.fromhex(hex_str) #转换为字节
reversed_bytes = hex_data[::-1] #反序

然后就可以使用u64(reversed_bytes.ljust(8,b’\x00’))对其处理在写入payload中

有师傅说可以直接用

1
int(p.recvline()[:-1],16)

直接一步进行处理,我自己还是见识太少了,另外师傅tql orz

puts的情况

构造ROP链来使puts函数泄露libc地址应该是最常见的情况了,puts函数也是输出的字符串,为什么就可以直接通过u64(p.recv(6).ljust(8,b’\x00’))来获得libc地址呢?

在构造payload输出的时候虽然输出的是字符串,但这个字符串本质是直接根据字节数据通过诸如ascii转换而来的,也就是,输出的数据仍是原来的字节,而不是像printf那样输出的是将每四位数据对应的字符将这个字符作为一个单独的字节输出

bins

0

global_max_fast

glibc(GNU C Library)中动态内存分配器(ptmalloc2) 的一个关键全局变量,用于控制 fast bins(快速分配区) 的最大内存块大小。

可以通过漏洞修改其大小来达到特殊的效果

fast bin

单向链表

32位下fast bin范围为16到64

64位下fast bin范围为32到128

从块分配器的角度来看,fastbin 中的块被视为已分配的块。它们仅在 malloc_consolidate 中批量与相邻对象合并。

double free

1
2
3
4
5
6
7
_int_free
-> if ((size) <= get_max_fast ())
-> unsigned int idx = fastbin_index(size);
-> fb = &fastbin (av, idx);
-> mchunkptr old = *fb, old2;
-> if (old == p)
-> malloc_printerr ("double free or corruption (fasttop)");

这里对double free漏洞的检查:

获取fastbin链表中最后入链的old,然后判断即将释放的fastbin和old的指针是否相同

unsorted bin

unsorted bins的bk和fd指向的是一个指针,而这个指针指向的是top chunk的地址,这个地址又储存在main_arena的0x58偏移处

main_arena - 0x10即为__malloc_hook的,__malloc_hook又是调用malloc时会调用的指针,所以可以通过劫持__malloc_hook来劫持程序控制流

然后通过fastbin attack将下一个堆块改为__malloc_hook - 0x23处,因为只有改为这处,视为堆块的此处的size位置才为正确的数据

binmap

Binmap的底层结构

  1. 物理存储
    binmapmalloc_state结构体中的一个数组(unsigned int binmap[BINMAPSIZE]),每个元素为32位无符号整数(unsigned int)。

    总长度:固定为4个int(即128位,对应63个large bin的索引需求)

    分组逻辑:每个int(32位)称为一个block,每个block负责管理32个连续的large bin状态

计算main_arena

  1. 通过上面的泄露unsorted_bin的fd指针

  2. 通过__malloc_trim函数直接获得

  3. 1

“比如把 .so 文件放到 IDA 中,找到 malloc_trim 函数,就可以获得偏移了。” -–ctfwiki

tcache

glibc 2.26后

两个结构体

tcache dup

仅限glibc2.26,可以直接对其进行double free

tcache_entry

1
2
3
4
5
6
/* We overlay this structure on the user-data portion of a chunk when
the chunk is stored in the per-thread cache. */
typedef struct tcache_entry
{
struct tcache_entry *next;
} tcache_entry;

一个单向链表

tcache_perthread_struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* There is one of these for each thread, which contains the
per-thread cache (hence "tcache_perthread_struct"). Keeping
overall size low is mildly important. Note that COUNTS and ENTRIES
are redundant (we could have just counted the linked list each
time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

# define TCACHE_MAX_BINS 64

static __thread tcache_perthread_struct *tcache = NULL;

通过counts记录tcache_entry上空闲的chunk数目

问题如下:

0

解决方法:

1

notepad C:\Windows\System32\drivers\etc\hosts

原因推测

clash系统代理时会更改本地hosts原因

随机数生成原理

rand()

C 库函数 int rand(void) 返回一个范围在 0 到 RAND_MAX 之间的伪随机数。

RAND_MAX 是一个常量,它的默认值在不同的实现中会有所不同,但是值至少是 32767。

srand()

C 库函数 void srand(unsigned int seed) 播种由函数 rand 使用的随机数发生器。

void srand(unsigned int seed)

参数seed是一个整形值,用于伪随机数生成算法播种。

伪随机数绕过方法

修改seed

有时随机数的种子seed会在栈上,可以通过栈溢出等方法对齐进行覆盖为已知值,再通过这个已知值获取对应的随机数序列

利用ctypes撞库

ctypes是Python内建的用于调用动态链接库函数的功能模块,通过调用该模块以及利用与题目相同的libc文件,来达到一个与题目中随机数生成方式相同的效果,实现撞库,这种方式下如果提前覆盖了指定种子,是更容易达成,如果直接设置时间种子撞库,可能存在时间对不上的情况,需要多测试几次。

使用方法:

1
2
3
4
5
from ctype import *
libc=cdll.LoadLibrary("libc.so.6")
a=[]
for i in range(50):
a.append(libc.rand())
1
2
3
#使用相同时间为种子
elf1=ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6")
elf1.srand(elf1.time(0))

何为exit_hook

hook即修改地址,exit_hook即修改exit函数内的地址达到控制执行流的目的

阅读全文 »

0

什么是fini_array

fini_array被__libc_csu_fini调用的函数的指针数组.一般可以在ida中用shift+f7找到

一般来讲这个数组有两个数据,但也有特例(只有一个数据)

阅读全文 »

何为格式化字符串漏洞

1
2
3
4
5
6
7
#include<stdio.h>
int main(){
` char buf[16];
gets(buf,0x400);
printf(buf);
return 0;
}

形如上述代码的程序一般具有格式化字符串漏洞,试想如果往buf里输入%p等格式化字符串时会发生什么

那么细心的师傅们可能就会说了:你这后面也没跟第二个参数啊,这能运行吗?

答案是肯定的,并且打印出的地址就是栈上的地址,这就是格式化字符串最简单的用法:泄露栈地址

那么还有什么别的用途吗,且听我继续分析

阅读全文 »