$ echo -n "NAME=${name}" | sha256sum
a81ac8ddf4d0d80ee6d3f4799277f5eda02b37167c6be4b6fd7021f2d39badde
忘了把标题删除了。
https://github.com/USTC-Hackergame/hackergame2021-writeups
date +%s 复制到 query param
看着图片把 hex-string 打出来,解码即可
用 Audacity 0.4 倍速播放,根据 NATO 字母表依次写出字母(和{})
考察 PHP 整数溢出
先 b9 = 9223372036854775807 (INT64_MAX)
结果 -9223372036854775808
如果加上大数使得结果变号,就会变成浮点数导致失败,所以先不要让他变号,输入 1024819115206086200 得到 -8,输入 1 得到 1
再输入一次 INT64_MAX 得到 -9223372036854775807
输入 1024819115206086200 得到 -7,此时差值 27 已经可以贝 9 整除,因此输入 3,得到 27
考察 ANSI 转义字符序列(就是能在终端输出彩色字的,还能移动光标、擦除、清屏等)
sed 把 [ 换成 \x1b[ 居然显示不出来?还把我净屏了
简单看点 https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
发现这个文件里面有以下几种元素:
^[39m^[<line>;<column>Htr ' ' '\n' < transparent.txt | sed 's/[[0-9;]*m//g' | grep -oP '\[[0-9
;]*H$' | grep -v '\[0;0' | xargs -I _ printf "\x1b[H\x1b%sW" _
即可播放动画

这游戏在网络上有点小火,玩法是根据一张照片挖掘信息
搜索关键词海边的肯德基,得到一个外墙颜色与照片完全一致的照片,确认该肯德基
X-Forwarded-For 伪造 IP
复制 curl,每发一次 sleep 两秒
强制罚站 10 分钟这是坏的
编译出的 ELF 文件 .data 和 .rodata 会被清零,这个好弄,只要全部是代码就会储存在 .text,从而不会被清零影响。
main(){
putchar(72);
putchar(101);
putchar(108);
putchar(108);
putchar(111);
putchar(44);
putchar(32);
putchar(119);
putchar(111);
putchar(114);
putchar(108);
putchar(100);
putchar(33);
}
编译出的程序代码段 .text 会被清零,而 _start 会被编译器放置在代码段。
经过挂 GDB 可发现并不是因为 00 非法指令导致程序崩溃,而是不停的循环跳转最终爆栈,因此只要在代码走过的地方中间插入一段 hello world 并退出即可。
main(){}
__attribute__((section(".text1")))
void test() {
__asm__(".byte 00\n" /* this zero is necesary */
"movl $13, %edx\n"
"movl $qqqxx, %ecx\n"
"movl $1, %ebx\n"
"movl $4, %eax\n"
"int $0x80\n"
"xorl %ebx, %ebx\n"
"movl $1, %eax\n"
"int $0x80\n"
"qqqxx:\n"
".ascii \"Hello, world!\""
);
}
先看下接口:
https://blog.yeswehack.com/yeswerhackers/how-exploit-graphql-endpoint-bug-bounty/
axios.post("/graphql", {
query: "{ user(id:1) { privateEmail } }"
})
p: $x, y$ 接近且 x prime,用 Wilson’s $(p-1)! \equiv p-1 \pmod{p}$ 倒着算
q: 解出 10 个质数的 multi-RSA
只需知道对应的 contract creation code 即可计算地址。
pragma solidity =0.8.9;
import './challenge1.sol';
contract Pwn is Predictor {
function predict(address challenge) external view returns (address) {
bytes32 seed = Challenge(challenge).seed();
bytes32 codehash = 0xfa9e82ddd8dbc9204f2d4547201e19c63d0c253a92ff97cc56ab55aff8eeb51e;
// by keccak'ing compiled contract creation code of challenge1:Dummy
bytes32 hash = keccak256(abi.encodePacked(
bytes1(0xff), challenge, seed, codehash
));
return address(uint160(uint(hash)));
}
}
使用一个不会被 revert 倒回的变量传递地址信息即可
pragma solidity =0.8.9;
import './challenge2.sol';
contract Helper {
function leak(address chall) public {
address created = Challenge(chall).create_child();
bytes memory encoded = abi.encodePacked(created);
revert(string(encoded));
}
}
contract Pwn is Predictor {
function predict(address chall) public returns (address) {
Helper h = new Helper();
try h.leak(chall) {
// nothing
} catch Error(string memory encoded) {
bytes memory mem = bytes(encoded);
address created = address(uint160(bytes20(mem)));
return created;
}
}
}
注意输入长度限制 4096,把没用的 revert 全部删除就能过了
导出 master key
sudo cryptsetup luksDump --dump-master-key /dev/loop1p1
xxd -p -r 保存 master.bin
sudo cryptsetup --master-key-file ./master.bin open /dev/loop1p1 day1
1、2 写法过于显然故略
sha256d 挖矿(草)
这个表达式很工整,手动解析然后把 / % 替换成 UDiv 和 URem,特例不用实现也能过,z3 去解,跑一会就能出 90 个了
#!/usr/bin/env python3
from pwn import remote
import re
import z3
token = "<token>"
syms = dict()
def genexpr(s):
global syms
if len(s) == 1:
if s == '0':
return 0
if s in syms:
return syms[s]
else:
x = z3.BitVec(s, 36)
syms[s] = x
return x
assert s.startswith('(') and s.endswith(')')
if s[1] == '-':
return - genexpr(s[2:-1])
i = 1
if s[i] == '(':
indent = 1
while indent > 0:
i += 1
if s[i] == '(': indent += 1
elif s[i] == ')': indent -= 1
i += 1
op = s[i]
if op == '+':
return genexpr(s[1:i]) + genexpr(s[i+1:-1])
elif op == '-':
return genexpr(s[1:i]) - genexpr(s[i+1:-1])
elif op == '*':
return genexpr(s[1:i]) * genexpr(s[i+1:-1])
elif op == '/':
return z3.UDiv(genexpr(s[1:i]), genexpr(s[i+1:-1]))
elif op == '%':
return z3.URem(genexpr(s[1:i]), genexpr(s[i+1:-1]))
else:
raise RuntimeError
def solve(left, right):
global syms
syms = dict()
e = genexpr(left)
s = z3.Solver()
s.add(e == right)
if s.check() == z3.sat:
model = s.model()
txt = ""
for name, var in syms.items():
value = str(model[var].as_long())
txt += f"{name}={value} "
return txt.strip()
if __name__ == "__main__":
io = remote("202.38.93.111", 10700)
io.sendlineafter("token:", token)
io.recvline()
skip = 0
for n in range(100):
expr = io.recvline().decode().rstrip()
val = io.recvline().decode().rstrip()
print(f"{n+1}# {expr} == {val}")
try:
left = expr
right = int(val)
solved = solve(left, right)
assert solved != None
print(f"[+] {solved}")
except:
#solved = input(f"{skip}# ")
solved = " "
if solved.strip() == "":
skip += 1
print(f"give up #{skip}")
if skip > 10:
exit(1)
io.sendline(solved)
io.interactive()
首先往里面放数字常数:
0 (x-x)
1 (x/x)
2 ((x/x)+(x/x))
2x+y ((((x/x)+(x/x))*{NC(x)})+{NC(y)}) or (((x/x)+(x/x))*{NC(x)}) if y == 0
选取适当的模数 k (7, 11, 13, 17, 19, 23) 可以使得 x+y 或者 x-y 互不同余,然后套 if 表达式进行插值,注意限制 4096
#!/usr/bin/env python3
from pwn import remote
token = "<token>"
x = 1
# Numeric Constant
def nc(i, r=2):
if i == 0:
return '(x-x)'
elif i == 1:
return '(x/x)'
elif i == 2:
return '((x/x)+(x/x))'
else:
ii = i // r
txt = '(' + nc(r) + '*' + nc(ii) + ')'
if i % r:
txt = '(' + txt + '+' + nc(i%r) + ')'
return txt
# INTERPolation
def interp(xya, mod):
op = None
mask = 2 ** 36 - 1
ss = list(map(lambda v3: ((v3[0] + v3[1]) & mask) % mod, xya))
if len(ss) == len(set(ss)):
op = '+'
vs = list(map(lambda v3: (((v3[0] + v3[1]) & mask) % mod, v3[2]), xya))
ss = list(map(lambda v3: ((v3[0] - v3[1]) & mask) % mod, xya))
if len(ss) == len(set(ss)):
op = '-'
vs = list(map(lambda v3: (((v3[0] - v3[1]) & mask) % mod, v3[2]), xya))
else:
return False
vs.sort(key = lambda v: v[0])
m = min(map(lambda v: v[1], vs))
def ifc(rem, i1, i2):
return f"if(((x{op}y)%{nc(mod)})<={nc(rem)},{i1},{i2})"
expr = nc(m) + '+' + ifc(vs[0][0], nc(vs[0][1] - m),
ifc(vs[1][0], nc(vs[1][1] - m),
ifc(vs[2][0], nc(vs[2][1] - m),
ifc(vs[3][0], nc(vs[3][1] - m),
nc(vs[4][1]-m)
)
)
)
)
def if_(rem, i1, i2):
return f"if((x{op}y)%{mod} <= {rem}, {i1}, {i2})"
hrexpr = str(m) + ' + ' + if_(vs[0][0], str(vs[0][1] - m),
if_(vs[1][0], str(vs[1][1] - m),
if_(vs[2][0], str(vs[2][1] - m),
if_(vs[3][0], str(vs[3][1] - m),
str(vs[4][1]-m)
)
)
)
)
print(hrexpr)
return expr
if __name__ == "__main__":
io = remote("202.38.93.111", 10800)
io.sendline(token)
for _ in range(10):
line = ""
while not line.startswith("Challenge"):
line = io.recvline().decode().strip()
print("[+]", line)
l5 = [io.recvline().decode().strip() for _ in range(5)]
xya = []
for l in l5:
x, y, a = map(lambda s: s[s.index("=")+1:].strip(), l.split(","))
print(x, y, a)
xya += [[int(x), int(y), int(a)]]
sent = False
for mod in (7, 11, 13, 17, 19, 23, 29):
expr = interp(xya, mod)
if expr:
io.sendline(expr)
sent = True
break
if not sent:
io.sendline(" ")
io.interactive()
确定磁盘的顺序,RAID 0 通过 file * 可知有分区表的那个是第一个,之后通过程序代码和 PDF 中的对象序号大致递增来确定剩下的顺序。块大小和偏移量都是 128 KB,组合成完整的磁盘镜像,发现是 XFS 直接 mount 之
#!/bin/bash
let offset="128 * 1024"
let blocksize="128 * 1024"
let total="16777216"
let max="total - blocksize"
disks=( \
"wlOUASom2fI.img" \
"jCC60mutgoE.img" \
"1GHGGrmaMM0.img" \
"5qiSQnlrA4Y.img" \
"d3Be7V0EVKo.img" \
"eRL2MQSdOjo.img" \
"RApjvIxRlu0.img" \
"ID7sM2RWkyI.img" \
)
let seek="0"
outfile="out.img"
while [ "$seek" -le "$max" ]
do
let seek="seek + blocksize"
for img in "${disks[@]}"
do
let t="seek+1"
tail -c +"$t" "$img" | head -c "$blocksize" >> "$outfile"
done
done
RAID 5 比 RAID 0 有点变化,块大小是 64 KB。file * 出来有两个都说自己是分区表,但其中那个每逢 0x50000 就出乱码的是因为与0异或导致出现了一模一样的内容,另一个是第一个盘。之后是实现 left symmetric layout,拼接数据,发现不能 mount,去掉前 1 MB 就可 mount 了
#!/bin/bash
let blocksize="65536"
let total="33554432"
disks=(\
"3RlmViivyG8.img" \
"IrYp6co7Gos.img" \
"3D8qN9DH91Q.img" \
"QjTgmgmwXAM.img" \
"60kE0MQisyY.img" \
)
let c20=0
let seek="0"
outfile="out.img"
# left symmetric
# http://www.reclaime-pro.com/posters/raid-layouts.pdf
while [ "$seek" -lt "$total" ]
do
let t="seek+1"
let i="c20 % 5"
img="${disks[$i]}"
#printf "%08x " "$seek"
#echo "img#$i"
tail -c +"$t" "$img" | head -c "$blocksize" >> "$outfile"
if test $c20 -eq 3 -o $c20 -eq 7 -o $c20 -eq 11 -o $c20 -eq 15 -o $c20 -eq 19
then
let seek="seek + blocksize"
fi
let c20="(c20 + 1) % 20"
done
tail -c +1048577 out.img > real.img
rm -f out.img
打上被盖掉的定位点,每个马赛克最多与 9 个块有关系,然后穷举,有的解不出来也没有关系,因为纠错是 H,扫一下就出 flag 了
#!/usr/bin/env python3
from math import floor
from itertools import product
import numpy as np
pix_size, nblks = 11, 627//11
px, py, mosn, mosize = 103, 137, 20, 23
varmap = dict()
class Var:
def __init__(self, sym):
self.id = sym
@property
def sol(self):
return self.id in varmap
@property
def val(self):
assert self.sol
return varmap[self.id]
@val.setter
def val(self, u8):
varmap[self.id] = u8
def set(self, u8):
varmap[self.id] = u8
def __eq__(self, right):
return self.id == right.id
class Expr:
def __init__(self):
self.vars = []
self.cnts = []
self.const = 0
def __iadd__(self, right):
if type(right) == Var:
var = right
if var in self.vars:
self.cnts[self.vars.index(var)] += 1
else:
self.vars += [var]
self.cnts += [1]
else:
self.const += right
return self
def __repr__(self):
s = ' + '.join(str(self.cnts[i]) + 'v' + str(self.vars[i].id) for i in range(len(self.vars)))
s += f' + {self.const}'
return s
@property
def num_free(self):
return sum(0 if var.sol else 1 for var in self.vars)
def value(self, vec):
s = self.const
assert len(vec) == self.num_free
n = 0
for i, var in enumerate(self.vars):
cnt = self.cnts[i]
if var.sol:
s += cnt * var.val
else:
s += cnt * vec[n]
n += 1
return s
from PIL import Image
im = Image.open("pixelated_qrcode.bmp")
ar = np.asarray(im, dtype='uint8')
def in_mos(xy):
x, y = xy
return (px <= x < px + mosn * mosize) and (py <= y < py + mosn * mosize)
def blkid(x, y):
xx, yy = x // pix_size, y // pix_size
return nblks * yy + xx
def blknid(i, j):
return nblks * j + i
def unblkid(i):
return (i % nblks) * pix_size, (i // nblks) * pix_size
def sample():
y = py - 1
for xx in range(px - 1, px + mosn * mosize + 2):
bid = blkid(xx, y)
if bid not in varmap:
varmap[bid] = ar[xx, y]
y = py + mosn * mosize
for xx in range(px - 1, px + mosn * mosize + 2):
bid = blkid(xx, y)
if bid not in varmap:
varmap[bid] = ar[xx, y]
x = px - 1
for yy in range(py - 1, py + mosn * mosize + 2):
bid = blkid(x, yy)
if bid not in varmap:
varmap[bid] = ar[x, yy]
x = px + mosn * mosize
for yy in range(py - 1, py + mosn * mosize + 2):
bid = blkid(x, yy)
if bid not in varmap:
varmap[bid] = ar[x, yy]
mos = []
avgs = []
def imports():
global mos, avgs
for i in range(mosn):
for j in range(mosn):
x1 = px + i*mosize
x2 = px + (i+1)*mosize
y1 = py + j*mosize
y2 = py + (j+1)*mosize
acc = Expr()
for u in range(x1, x2):
for v in range(y1, y2):
v = Var( blkid(u, v) )
if v.sol:
acc += v.val
else:
acc += v
mos += [acc]
avgs += [ar[x1, y1]]
def put_eye(ci, cj):
i = ci - 2
for j in range(cj-2, cj+3):
varmap[ blknid(i, j) ] = 0
i = ci + 2
for j in range(cj-2, cj+3):
varmap[ blknid(i, j) ] = 0
j = cj - 2
for i in range(ci-2, ci+3):
varmap[ blknid(i, j) ] = 0
j = cj + 2
for i in range(ci-2, ci+3):
varmap[ blknid(i, j) ] = 0
for di, dj in product((-1, 0, 1), repeat=2):
varmap[ blknid(ci + di, cj + dj) ] = 255
varmap[ blknid(ci, cj) ] = 0
def eyes():
put_eye(28, 28)
put_eye(28, 50)
put_eye(50, 28)
put_eye(50, 50)
eyes()
sample()
imports()
def times(n):
return product([0, 255], repeat=n)
def solve_one():
global mos
for i, equ in enumerate(mos):
nv = equ.num_free
if nv == 0:
continue
avg = avgs[i]
nsols, solv = 0, None
for vec in times(nv):
s = equ.value(vec)
a = floor(s / (mosize ** 2))
if a == avg:
nsols += 1
solv = vec
if nsols == 1:
n = 0
for var in equ.vars:
if not var.sol:
varmap[var.id] = solv[n]
n += 1
return True
raise RuntimeError
if __name__ == '__main__':
try:
while True:
solve_one()
except:
print('daijoubu')
copyar = np.array(ar)
for bid, color in varmap.items():
bx, by = unblkid(bid)
copyar[bx:bx+pix_size, by:by+pix_size] = color
fix = Image.fromarray(copyar, mode='L')
fix.save("fixed.bmp")
加密是修改过的 TEA
F12,Deactivate breakpoints 或者 Save as override 去掉闹事的 debugger 语句
遇到源码不知道的关键词直接打在 Console 里获取
const encs = [
'6fbde674819a59bf',
'a12092565b4ca2a7',
'a11dc670c678681d',
'af4afb6704b82f0c'
]
const key = '1356853149054377';
function TEA_encrypt(plain, k4) {
let v0 = plain[0], v1 = plain[1];
const delta = 0x9E3779B9, ss = delta * 32;
let sum = 0x0;
while (sum != ss) {
v0 += (v1 << 0x4 ^ v1 >>> 0x5) + v1 ^ sum + k4[sum & 0x3],
sum += delta,
v1 += (v0 << 0x4 ^ v0 >>> 0x5) + v0 ^ sum + k4[sum >>> 0xb & 0x3];
}
return [v0, v1];
}
function TEA_decrypt(cipher, k4) {
let v0 = cipher[0], v1 = cipher[1];
const delta = 0x9E3779B9, ss = delta * 32;
let sum = ss;
while (sum != 0) {
v1 -= (v0 << 0x4 ^ v0 >>> 0x5) + v0 ^ sum + k4[sum >>> 0xb & 0x3];
sum -= delta;
v0 -= (v1 << 0x4 ^ v1 >>> 0x5) + v1 ^ sum + k4[sum & 0x3];
}
return [v0, v1];
}
function Str4ToLong(s) {
let sum = 0x0;
for (let i = 0x0; i < 4; i++)
sum |= s.charCodeAt(i) << i * 8;
return isNaN(sum) ? 0x0 : sum;
}
let k = new Array(4);
for (let i = 0; i < 4; i++) k[i] = Str4ToLong(key.slice(i * 4, (i + 1) * 4));
function Base16ToLong(hex) {
return parseInt('0x' + hex);
}
function ToText(i) {
const f = String.fromCharCode;
return [f(i >>> 24), f((i >>> 16) & 0xff), f((i >>> 8) & 0xff), f(i & 0xff)].reverse().join('')
}
out = ''
for (let enc of encs) {
let v0 = Base16ToLong(enc.slice(0, 8)),
v1 = Base16ToLong(enc.slice(8, 16));
let [c0, c1] = TEA_decrypt([v0, v1], k);
out += ToText(c0)+ToText(c1);
}
console.log(out);
经过 afl 巨大多挖矿可以发现:
S100 申必咒语,拥有执行 shellcode 的能力。用 x64 execve
#!/usr/bin/env python3
from z3 import *
from PIL import Image
im = Image.open("deng.png")
x, y, inc = 64, 64, 115
arr = []
for _1 in range(12):
row = []
x = 64
for _2 in range(12):
row += [ im.getpixel((x, y))[0] ]
x += inc
arr += [row]
y += inc
vars = []
for y in range(12):
for x in range(12):
vars += [ BitVec("v" + str(12*y+x), 8) ]
slots = []
for y in range(12):
row = []
for x in range(12):
row += [ 0 ]
slots += [row]
s = Solver()
for y in range(12):
for x in range(12):
l3 = [(3, y, x)]
l2 = list((2, *u) for u in filter(lambda u: 0 <= u[0] < 12 and 0 <= u[1] < 12, [(y-1, x), (y+1, x), (y, x-1), (y, x+1)]))
l1 = list((1, *u) for u in filter(lambda u: 0 <= u[0] < 12 and 0 <= u[1] < 12, [(y-2, x), (y+2, x), (y, x-2), (y, x+2)]))
l = l3 + l2 + l1
acc = 0
for t, u, v in l:
acc += t * vars[12*u+v]
s.add(acc == arr[y][x])
if s.check() == sat:
model = s.model()
taps = []
for var in vars:
taps += [ model[var].as_long() ]
print(taps)
https://t.me/hackergame2021 (大草)
Newer: PKU GeekGame 1st writeup
Older: HITCTF 2020 crypto writeup