2019 安洵杯 Re 部分WP
0x01.EasyEncryption
测试文件:https://www.lanzous.com/i7soysb
1.IDA打开
int sub_416560()
{
int v0; // eax
int v1; // edx
int v2; // edx
int v3; // ecx
int v4; // ST08_4
char v6[]; // [esp+310h] [ebp-7E0h]
char Str; // [esp+700h] [ebp-3F0h]
int v8; // [esp+AECh] [ebp-4h]
int savedregs; // [esp+AF0h] [ebp+0h] sub_41132A((int)&unk_424091);
sub_411294(std::cout, "输入flag: ");
sub_41113B(std::cin, &Str);
sub_4111F4(&Str, (int)v6); // v6="artqkoehqpkbihv"
if ( (unsigned __int8)sub_4112B7(v6) )
{
sub_41105F("you are right\n");
system("pause");
sub_411339(v3, v2);
}
else
{
v0 = sub_411294(std::cout, "wrong");
sub_41114A(v0, );
}
v4 = v1;
sub_41137A(&savedregs, &dword_416660, );
return sub_411339((unsigned int)&savedregs ^ v8, v4);
}
2.代码分析
输入flag之后,首先经过sub_4111F4(&Str, (int)v6)函数处理,再经过sub_4112B7(v6)函数判断。因为v6未知,所以首先通过最后一个函数找到v6
2.1 sub_4112B7函数分析
打开sub_4112B7函数
int __cdecl sub_413980(char *Str)
{
int v1; // edx
int v2; // ecx
int v3; // edx
int v4; // ecx
int v5; // edx
int v6; // ecx
char *Str1; // [esp+D0h] [ebp-8h] sub_41132A((int)&unk_42405B);
Str1 = (char *)sub_4112D5(Str); // Base64
if ( !j_strcmp(Str1, "YXJ0cWtvZWhxcGtiaWh2") )
{
free(Str1);
sub_411339(v6, v5);
}
else
{
free(Str1);
sub_411339(v2, v1);
}
return sub_411339(v4, v3);
}
通过分析得到,sub_4112D5函数是Base6加密,因此这个函数实际上就是将Str进行Base64加密,得到字符串Str1与"XJ0cWtvZWhxcGtiaWh2"比较。将"XJ0cWtvZWhxcGtiaWh2"进行Base64解密,得到Str,即v6="artqkoehqpkbihv"
2.2 sub_4111F4函数分析
接着,打开sub_4111F4函数,太长了,就不贴出来了。这个函数,分为三个部分:
0x01 第一部分:
for ( i = ; byte_41EC80[i]; ++i )
{
v16 = (unsigned __int8)byte_41EC80[i] - ;
v2 = j_abs(v16);
v15[i] = v2;
}
byte_41EC80已知,可以写出脚本变换,得到v15
Serial = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" Li = [] for i in Serial:
Li.append(abs(ord(i) - 97)
0x02 第二部分
v10 = j_strlen(Str);
if ( v10 % i <= )
{
v3 = v10 % i; // 余数
v9 = v10 / i; // 商
}
else
{
v3 = v10 % i;
v9 = v10 / i + ;
}
0x03 第三部分
for ( j = ; j < v9; ++j )
{
for ( k = ; k < i; ++k )
{
if ( Str[v14] ) // 输入的字符值不能为0
{
if ( Str[v14] < || Str[v14] > ) // 输入只能是小写字母
exit();
if ( (Str[v14] + v15[k] - ) % + > )
v4 = (Str[v14] + v15[k] - ) % + ;
else
v4 = (Str[v14] + v15[k] - ) % + ;
v8 = v4;
v3 = v14 + a2;
*(_BYTE *)(v14++ + a2) = v8;
}
}
}
第二部分,第三部分和v6,结合分析,可以得到输入的flag应该是15,那么v9=1,v3=15,而第三部分实际上就是一个根据输入字符,进行选择变换得到v14(即v6),因为输入只能是小写字母,那我逆向变换的时候,只要得到的是小写字母,那就行了
3.脚本解密
Serial = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" Li = [] for i in Serial:
Li.append(abs(ord(i) - 97))
print(Li) t1 = len(Li) - 1 enc = "artqkoehqpkbihv"
v10 = len(enc)
v14 = 0
Str = ''
for j in range(1):
for k in range(v10):
for n in range(10):
tmp1 = ord(enc[v14]) - 71 + 26 * n + 97 - Li[k]
tmp2 = ord(enc[v14]) - 97 + 26 * n + 97 - Li[k]
if tmp1 >= 97 and tmp1 <= 122:
print ("SUCCESS!")
Str += chr(tmp1)
break
elif tmp2 >= 97 and tmp2 <= 122:
print ("SUCCESS!")
Str += chr(tmp2)
break
else:
print("ERROR!")
v14 = v14 + 1
print (Str)
4.get flag!
flag{umpnineissogood}
0x02 crackme
测试文件:https://www.lanzous.com/i7sucre
1.函数定位
首先定位到起始的函数sub_412AB0
int __stdcall sub_412AB0(int a1, int a2, int a3, int a4)
{
size_t i; // [esp+D8h] [ebp-8h] for ( i = ; i < j_strlen(BASE64_table_41A080); ++i )
{
if ( BASE64_table_41A080[i] <= && BASE64_table_41A080[i] >= )
{
BASE64_table_41A080[i] -= ;
}
else if ( BASE64_table_41A080[i] <= && BASE64_table_41A080[i] >= )
{
BASE64_table_41A080[i] += ;
}
}
MessageBoxA(, "hooked", "successed", );
AddVectoredExceptionHandler(, Handler);
return ;
}
2.程序分析
2.1 Base64变表1
根据起始函数的第一个for循环,这里首先对Base64的表进行的变换,将大小写进行对换。
#include <iostream>
#include <string> using namespace std; string Str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int main()
{
char v2; size_t i; // [esp+D8h] [ebp-8h] for (i = ; i < Str.length(); ++i)
{
if (Str[i] <= && Str[i] >= )
{
Str[i] -= ;
}
else if (Str[i] <= && Str[i] >= )
{
Str[i] += ;
}
} cout << Str << endl; system("PAUSE");
return ;
}
Base64大小写变换
得到新base64表"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
2.2 向量化异常处理
SetUnhandledExceptionFilter()函数:https://baike.baidu.com/item/SetUnhandledExceptionFilter/2334228?fr=aladdin
AddVectoredExceptionHandler()函数:https://www.cnblogs.com/suanguade/p/6674232.html
接着使用向量化异常处理,进入 Handler
int __stdcall Handler_0(_DWORD **a1)
{
char v2; // [esp+D0h] [ebp-18h]
char v3; // [esp+D1h] [ebp-17h]
char v4; // [esp+D2h] [ebp-16h]
char v5; // [esp+D3h] [ebp-15h]
char v6; // [esp+D4h] [ebp-14h]
char v7; // [esp+D5h] [ebp-13h]
char v8; // [esp+D6h] [ebp-12h]
char v9; // [esp+D7h] [ebp-11h]
char v10; // [esp+D8h] [ebp-10h]
char v11; // [esp+D9h] [ebp-Fh]
char v12; // [esp+DAh] [ebp-Eh]
char v13; // [esp+DBh] [ebp-Dh]
char v14; // [esp+DCh] [ebp-Ch]
char v15; // [esp+DDh] [ebp-Bh]
char v16; // [esp+DEh] [ebp-Ah]
char v17; // [esp+DFh] [ebp-9h] if ( **a1 == - )
{
v2 = 'w';
v3 = 'h';
v4 = 'e';
v5 = 'r';
v6 = 'e';
v7 = '_';
v8 = 'a';
v9 = 'r';
v10 = 'e';
v11 = '_';
v12 = 'u';
v13 = '_';
v14 = 'n';
v15 = 'o';
v16 = 'w';
v17 = '?';
sm4_func((int)&unk_41A218, (int)&v2);
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TopLevelExceptionFilter);
}
return ;
}
2.2.1 sm4加密
pysm4包下载安装教程:https://www.cnblogs.com/Mayfly-nymph/p/11939336.html
进入sm4_func函数(这个我改过名了)
int __cdecl sub_411F50(int a1, unsigned int *a2)
{
unsigned int v2; // eax
int v3; // ecx
int result; // eax
unsigned int v5; // [esp+D0h] [ebp-B8h]
unsigned int v6; // [esp+DCh] [ebp-ACh]
unsigned int v7; // [esp+E0h] [ebp-A8h]
unsigned int v8; // [esp+E4h] [ebp-A4h]
int v9; // [esp+E8h] [ebp-A0h]
int v10[]; // [esp+ECh] [ebp-9Ch]
unsigned int v11; // [esp+174h] [ebp-14h]
unsigned int v12; // [esp+178h] [ebp-10h]
unsigned int v13; // [esp+17Ch] [ebp-Ch]
unsigned int v14; // [esp+180h] [ebp-8h] v5 = ;
v11 = _byteswap_ulong(*a2);
v12 = _byteswap_ulong(a2[]);
v13 = _byteswap_ulong(a2[]);
v2 = _byteswap_ulong(a2[]);
v14 = v2;
v6 = v11 ^ 0xA3B1BAC6;
v7 = dword_417A68[] ^ v12;
v8 = dword_417A68[] ^ v13;
v3 = dword_417A68[] ^ v2;
result = ;
v9 = v3;
while ( v5 < 0x20 )
{
v10[v5] = *(&v6 + v5) ^ sub_4114E0(dword_417A78[v5] ^ *(&v9 + v5) ^ *(&v8 + v5) ^ *(&v7 + v5));
*(_DWORD *)(a1 + * v5) = v10[v5];
result = v5++ + ;
}
return result;
}
Google搜了下0xA3B1BAC6
发现了这个函数应该是sm4加密
2.2.2 异常处理
接着,使用SetUnhandledExceptionFilter捕获异常,进入TopLevelExceptionFilter
_DWORD *__cdecl sub_412C30(_DWORD *a1)
{
_DWORD *result; // eax
char v2; // STD7_1
size_t i; // [esp+DCh] [ebp-8h] result = a1;
if ( *(_DWORD *)*a1 == - )
{
for ( i = ; i < j_strlen(Str2); i += )
{
v2 = Str2[i];
Str2[i] = Str2[i + ];
Str2[i + ] = v2;
}
Str1 = sub_41126C(byte_41A180);
*(_DWORD *)(a1[] + ) = *(_DWORD *)(*a1 + );
*(_DWORD *)(a1[] + ) = *(_DWORD *)(*a1 + );
*(_DWORD *)(a1[] + ) = *(_DWORD *)(*a1 + );
*(_DWORD *)(a1[] + ) = *(_DWORD *)(*a1 + );
*(_DWORD *)(a1[] + ) = *(_DWORD *)(*a1 + );
*(_DWORD *)(a1[] + ) = *(_DWORD *)(*a1 + );
*(_DWORD *)(a1[] + ) = sub_411136;
result = (_DWORD *)-;
}
return result;
}
第一个for循环就是在将Str2相邻两位互换
#include <iostream>
#include <string> using namespace std; string Str2 = "1UTAOIkpyOSWGv/mOYFY4R!!"; int main()
{
char v2; for (int i = ; i < Str2.length(); i += )
{
v2 = Str2[i];
Str2[i] = Str2[i + ];
Str2[i + ] = v2;
} cout << Str2 << endl; system("PAUSE");
return ;
}
Str2相邻位变换
接着通过sub_41126C函数传入byte_41A180,返回值为Str2,最后将Str1与Str2比较是否相同,输出success或者error,这就很明显了,肯定要输出‘success’,那Str2就要等于'U1ATIOpkOyWSvGm/YOYFR4!!'
2.2.3 Base64变表2
打开sub_41126C函数
_BYTE *__cdecl sub_413090(char *Str)
{
int k; // [esp+E4h] [ebp-5Ch]
int v3; // [esp+F0h] [ebp-50h]
signed int j; // [esp+FCh] [ebp-44h]
int v5; // [esp+108h] [ebp-38h]
signed int i; // [esp+114h] [ebp-2Ch]
_BYTE *v7; // [esp+120h] [ebp-20h]
signed int v8; // [esp+12Ch] [ebp-14h]
int v9; // [esp+138h] [ebp-8h] v5 = ;
v8 = j_strlen(Str); // Str长度
if ( v8 % ) // 设置字节长度
v9 = * (v8 / ) + ;
else
v9 = * (v8 / );
v7 = malloc((v9 + ) | -__CFADD__(v9, )); // 申请空间
v7[v9] = ;
for ( i = ; i < v8; i += ) // 三位三位的处理,base64?
{
v3 = ;
for ( j = ; j < ; ++j )
v3 |= (unsigned __int8)Str[j + i] << * ( - j);
for ( k = ; k < ; ++k )
{
if ( k >= - (i + - v8) && i + > v8 )// 长度大于等于4的部分使用'!'补齐
v7[v5] = '!';
else
v7[v5] = ::Str[sub_4110FF((v3 >> * ( - k)) & 0x3F)];
++v5;
}
}
return v7;
}
在函数sub_4110FF有一步不同的操作
int __cdecl sub_412760(int a1)
{
return (a1 + ) % ;
}
这里+24,相当于变表的值为原表取值右移24位
Str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/" Str1 = '' for i in Str:
Str1 += Str[(Str.find(i)+24) % len(Str)]
print (Str1)
得到新的Base64表"yzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvwx"
到这我们整个过程就分析完毕了,接下来只需要逆向操作就行了。
3.逆向解密
# encoding: utf-8
from pysm4 import decrypt,encrypt
from base64 import b64decode Str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
Str_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" Str1 = '' # 变换之后的Base64表
for i in Str:
Str1 += Str[(Str.find(i)+24) % len(Str)]
print (Str1) # 之前是用变换之后的表进行Base64加密
# 现在转换为正常的Base64加密,
# 再解密得到sm4加密之后的字符串
enc = "U1ATIOpkOyWSvGm/YOYFR4!!"
dec1 = ''
for i in range(len(enc)):
if enc[i] == '!':
dec1 += '='
else:
dec1 += Str_[Str1.find(enc[i])]
print (dec1) dec2 = b64decode(dec1)
print (dec2) import codecs # 先进行hex编码,再转换为十进制整型
encode_hex = codecs.getencoder("hex_codec")
dec3 = int(encode_hex(dec2)[0],16)
print (dec3) # 将key转换为十进制
key = "where_are_u_now?"
key = int(encode_hex(key)[0],16)
print (key) # 将解密的字符串转换会hex
decode_hex = codecs.getdecoder("hex_codec")
dec4 = decrypt(dec3, key)
dec4 = str(hex(dec4)[2:-1]) print (decode_hex(dec4)[0])
4. get flag!
flag{SM4foRExcepioN?!}
官方公布源码:https://github.com/D0g3-Lab/i-SOON_CTF_2019
其他WP:https://blog.csdn.net/Zarcs_233/article/details/103338006
这次进入线下,希望能够好好发挥,加油!
2019 安洵杯 Re 部分WP的更多相关文章
- [安洵杯 2019]iamthinking&&thinkphp6.0反序列化漏洞
[安洵杯 2019]iamthinking&&thinkphp6.0反序列化漏洞 刚开始是403,扫描以下目录,扫描到三个目录. [18:06:19] 200 - 1KB - /REA ...
- 安洵杯iamthinking(tp6反序列化链)
安洵杯iamthinking tp6pop链 考点: 1.tp6.0反序列化链 2.parse_url()绕过 利用链: 前半部分利用链(tp6.0) think\Model --> __des ...
- buuctfweb刷题wp详解及知识整理----[安洵杯 2019]easy_web
尝试之路加wp 观察源代码和get所传参数可猜测img所传参数img就是该图片经过两次base64编码和一次hex编码后可得555.png成果验证猜测 然后发现该图片以data元数据封装的方式放到了源 ...
- [安洵杯 2019]easy_serialize_php
0x00 知识点 PHP反序列化的对象逃逸 任何具有一定结构的数据,只要经过了某些处理而把自身结构改变,则可能会产生漏洞. 参考链接: https://blog.csdn.net/a3320315/a ...
- [安洵杯 2019]easy_web
0x00 知识点 md5强类型的绕过 方法比较固定: POST: a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%d ...
- 刷题[安洵杯 2019]easy_web
前置知识 md5碰撞: %4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e% ...
- [安洵杯 2019]easy_web-1
1.首先打开题目如下: 2.观察访问的地址信息,发现img信息应该是加密字符串,进行尝试解密,最终得到img名称:555.png,如下: 3.获得文件名称之后,应该想到此处会存在文件包含漏洞,因为传输 ...
- 2021美团安洵暗泉re部分复现
typora-copy-images-to: ./ 安洵杯 sign_in 贪吃蛇 虽然没啥用 smc解密拿一下flag相关的部分 倒着看看sub_40105F 和sub_401055函数 写出解密算 ...
- 【CTF】2019湖湘杯 miscmisc writeup
题目来源:2019湖湘杯 题目链接:https://adworld.xctf.org.cn/task/answer?type=misc&number=1&grade=1&id= ...
随机推荐
- php+大文件上传
1.使用PHP的创始人 Rasmus Lerdorf 写的APC扩展模块来实现(http://pecl.php.net/package/apc) APC实现方法: 安装APC,参照官方文档安装,可以使 ...
- OverFeat:基于卷积网络的集成识别、定位与检测
摘要:我们提出了一个使用卷积网络进行分类.定位和检测的集成框架.我们展示了如何在ConvNet中有效地实现多尺度和滑动窗口方法.我们还介绍了一种新的深度学习方法,通过学习预测对象边界来定位.然后通过边 ...
- [spring jpa] 解决SimpleJpaRepository的多数据源配置问题
前言 前段时间使用spring jpa做了一个项目,由于涉及到了多个数据库,因此需要进行多数据源的配置.网上找了很多的资料,尝试着配置,都以失败告终.之后通过断点最终完成了多数据源的配置.这篇博客主要 ...
- C++二维数组(指针)做参数
一.问题描述 使用C++编程过程中经常需要使用到二维数组,然而初级程序员在使用过程中经常会出错使程序崩溃.下面就二维指针的定义,初始化,以及二维指针做参数给出简单介绍. 1.二维数组的定义与初始化 在 ...
- Oracle-分配用户只读存储过程权限
系统新来了系统运维人员,要求创建数据库账号,只分配对表,视图,存储程序有只读权限 因为表和视图权限接触比较频繁,所以今天花点时间整理下关于存储过程的权限 关于ORACLE账号的权限问题,一般分为两种权 ...
- bootstrap editable初始化后表单可修改数据
function loadData() { var url = "${ctx }/sys/marketing/product/page"; $('#tablepager').boo ...
- POJO / Javabean / Entity Bean
POJO 和JavaBean是我们常见的两个关键字,一般容易混淆,POJO全称是Plain Ordinary Java Object / Pure Old Java Object,中文可以翻译成:普通 ...
- Centos 7 Redmine 安装,粘贴图片插件安装
转自: https://blog.csdn.net/jctian000/article/details/80591878 Redmine 是一个开源的.基于Web的项目管理和缺陷跟踪工具.它用日历和甘 ...
- Nginx常见配置说明
#定义Nginx运行的用户和用户组 user www www; #nginx进程数,建议设置为等于CPU总核心数. worker_processes 8; #全局错误日志定义类型,[ debug | ...
- 洛谷 P3865 ST表
ST表 ST表的功能很简单 它是解决RMQ问题(区间最值问题)的一种强有力的工具 它可以做到O(nlogn)预处理,O(1)查询最值 是一种处理静态区间可重复计算问题的数据结构,一般也就求求最大最小值 ...