2019 上海市大学生网络安全大赛 RE部分WP
这次比赛就做了这一道逆向题,看到队友的WP,下面的对v10的加密方式为RC4,从我提取的v4数组就能够察觉出这是CR4了,自己傻乎乎的用OD调试,跟踪数据半天才做出来,还是见得的少了... ...下面有几篇不错的RC4的文章:
C语言实现:https://zhoujianshi.github.io/articles/2016/RC4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/index.html
Python实现:https://specters.top/2019/03/01/Python%E5%AE%9E%E7%8E%B0RC4/
文件下载:https://www.lanzous.com/i76vcne
准备
获取信息
- 32位文件
IDA打开
int __cdecl main(int argc, const char **argv, const char **envp)
{
_DWORD *v4; // [esp+1Ch] [ebp-A0h]
int v5; // [esp+20h] [ebp-9Ch]
int v6; // [esp+24h] [ebp-98h]
int v7; // [esp+28h] [ebp-94h]
int v8; // [esp+2Ch] [ebp-90h]
char v9; // [esp+30h] [ebp-8Ch]
int v10; // [esp+50h] [ebp-6Ch]
int v11; // [esp+70h] [ebp-4Ch] sub_402620();
v5 = ;
v6 = ;
v7 = ;
v8 = ;
v4 = malloc(0x408u);
puts("Plz solve the puzzle:");
sub_40ED00("%32s", &v9);
if ( (unsigned __int8)sub_401C70(&v9)
&& (sub_401B60((int)&v10, &v9),
sub_401850(v4, (int)&v5, strlen((const char *)&v5)),
sub_4018D0(v4, &v10, ),
(unsigned __int8)sub_401950(&v10)) )
{
sub_401BA0(&v9, (int)&v11);
sub_40ED20("Congrats!\n%s\n", &v11);
}
else
{
puts("Failed!");
}
return ;
}
通过分析,要得到flag,则需要if中的条件成立
if ( (unsigned __int8)sub_401C70(&v9)
&& (sub_401B60((int)&v10, &v9),
sub_401850(v4, (int)&v5, strlen((const char *)&v5)),
sub_4018D0(v4, &v10, ),
(unsigned __int8)sub_401950(&v10)) )
代码分析
打开sub_401C70(&v9)函数
int __cdecl sub_401C70(char *a1)
{
char *v2; // edx if ( strlen(a1) != )
return ;
v2 = a1;
while ( (unsigned __int8)(*v2 - ) > 38u && (unsigned __int8)(*v2 - ) <= 54u )// 输入的字符串每个字符都是'a'~'f'或者'0'~'9'之间
{
if ( ++v2 == a1 + ) // 遍历到字符串末尾结束,输入字符串长度为16
return ;
}
return ;
}
暴力解v10
因为&&符号后面是( func1, func2, func3)形式的判断条件,因此我先直接看func3即可(func3决定条件是否成立),打开sub_401950(&v10)函数
bool __cdecl sub_401950(_BYTE *a1)
{
_BYTE *v1; // ecx
bool result; // al v1 = a1;
while ( )
{
switch ( *v1 )
{
case :
dword_40F028 &= dword_40F038;
dword_40F02C *= dword_40F028;
goto LABEL_4;
case :
if ( !dword_40F02C )
goto LABEL_6;
dword_40F028 /= dword_40F02C;
dword_40F024 += dword_40F034;
goto LABEL_4;
case :
dword_40F030 ^= dword_40F034;
dword_40F03C += dword_40F020;
goto LABEL_4;
case :
dword_40F03C -= dword_40F030;
dword_40F030 &= dword_40F024;
goto LABEL_4;
case :
dword_40F034 *= dword_40F020;
dword_40F02C -= dword_40F038;
goto LABEL_4;
case :
dword_40F020 ^= dword_40F02C;
dword_40F038 -= dword_40F03C;
goto LABEL_4;
case :
if ( !dword_40F03C )
goto LABEL_6;
dword_40F034 |= dword_40F024 / dword_40F03C;
dword_40F024 /= dword_40F03C;
goto LABEL_4;
case :
dword_40F038 += dword_40F028;
dword_40F034 |= dword_40F024;
goto LABEL_4;
case :
dword_40F020 *= dword_40F02C;
dword_40F030 -= dword_40F03C;
goto LABEL_4;
case :
dword_40F028 += dword_40F034;
dword_40F02C ^= dword_40F030;
LABEL_4:
if ( ++v1 != a1 + )
continue;
result = (dword_40F038 == )
+ (dword_40F034 == )
+ (dword_40F030 == )
+ (dword_40F02C == -)
+ (dword_40F028 == )
+ (dword_40F024 == )
+ (dword_40F020 == -) == ;
if ( dword_40F03C != - )
goto LABEL_6;
break;
default:
LABEL_6:
result = ;
break;
}
return result;
}
}
通过分析,这就是将v10的每一位进行switch选择,对一组数据进行更改,最后要满足要求,才能返回1。
因此,得到v10的值成为了这道题的关键,又因为v10的每个数都是0~9之间,且长度为8,我直接暴力求解。
#include <bits/stdc++.h> #pragma warning(disable:4996)
#define _DWORD unsigned int
#define _BYTE char using namespace std; bool __cdecl sub_401950(_BYTE* a1)
{
_BYTE* v1; // ecx
bool result; // al int val_0 = 0x0A;
int val_1 = 0x8A;
int val_2 = 0x1A1;
int val_3 = 0x12A;
int val_4 = 0x269;
int val_5 = 0x209;
int val_6 = 0x68;
int val_7 = 0x39F;
int val_8 = 0x2C8; v1 = a1;
while ()
{
switch (*v1-)
{
case :
val_3 &= val_7;
val_4 *= val_3;
goto LABEL_4;
case :
if (!val_4)
goto LABEL_6;
val_3 /= val_4;
val_2 += val_6;
goto LABEL_4;
case :
val_5 ^= val_6;
val_8 += val_1;
goto LABEL_4;
case :
val_8 -= val_5;
val_5 &= val_2;
goto LABEL_4;
case :
val_6 *= val_1;
val_4 -= val_7;
goto LABEL_4;
case :
val_1 ^= val_4;
val_7 -= val_8;
goto LABEL_4;
case :
if (!val_8)
goto LABEL_6;
val_6 |= val_2 / val_8;
val_2 /= val_8;
goto LABEL_4;
case :
val_7 += val_3;
val_6 |= val_2;
goto LABEL_4;
case :
val_1 *= val_4;
val_5 -= val_8;
goto LABEL_4;
case :
val_3 += val_6;
val_4 ^= val_5;
LABEL_4:
if (++v1 != a1 + )
continue;
result = (val_7 == )
+ (val_6 == )
+ (val_5 == )
+ (val_4 == -)
+ (val_3 == )
+ (val_2 == )
+ (val_1 == -) == ;
if (val_8 != -)
goto LABEL_6;
break;
default:
LABEL_6:
result = ;
break;
}
return result;
}
} int main()
{
char* s = (char*)malloc(); for (long i = ; i < ; ++i) {
sprintf(s, "%08ld", i);
//cout << s << endl;
if (sub_401950(s)) {
cout << i << endl;
system("PAUSE");
}
}
system("PAUSE");
return ;
}
得到v10的值
v9的十六进制加密
得到v10值后,不能获得有关输入字符串的相关信息,打开前面的函数sub_401B60(&v10, &v9)
int __cdecl sub_401B60(int a1, _BYTE *a2)
{
_BYTE *v2; // ebx
int i; // esi
char v4; // ST08_1
_BYTE *v5; // ST00_4
int result; // eax v2 = a2;
for ( i = a1; *v2; result = sub_40ED40(v5, "%02X", v4) )
{
v4 = i;
v5 = v2;
v2 += ;
++i;
}
return result;
}
能够猜测到,v9加密为十六进制,存储到v10中,(如果v9="0123456789abcdef",则v10=0x0123456789abcdef。通过后面OD调试,也能够发现。)
v4数组生成
打开sub_401850(v4, &v5, strlen((const char *)&v5))函数
int __cdecl sub_401850(_DWORD *a1, int a2, int a3)
{
int v3; // eax
int v4; // edi
unsigned int *v5; // ebx
int v6; // ecx
int result; // eax
unsigned int v8; // esi
char v9; // dl
unsigned int *v10; // edx v3 = ;
v4 = (int)(a1 + );
*a1 = ;
a1[] = ;
do
{
*(_DWORD *)(v4 + * v3) = v3;
++v3;
}
while ( v3 != );
v5 = a1 + ;
v6 = ;
LOBYTE(result) = ;
do
{
v8 = *v5;
v9 = *(_BYTE *)(a2 + v6++) + *v5;
result = (unsigned __int8)(v9 + result);
v10 = (unsigned int *)(v4 + * result);
*v5 = *v10;
*v10 = v8;
if ( v6 >= a3 )
v6 = ;
++v5;
}
while ( v5 != a1 + );
return result;
}
这里在生成V4数组,我从OD中提取出来
0x00, 0x00, 0x71, 0x12,
0x62, 0x31, 0x4D, 0x97,
0x14, 0x0D, 0xED, 0xA3,
0xD6, 0xFC, 0xF1, 0x3B,
0x3C, 0x33, 0xB5, 0x22,
0xA2, 0x1A, 0x17, 0x1D,
0x98, 0x91, 0x06, 0x2A,
0x8B, 0x23, 0xE6, 0x55,
0x46, 0x3A, 0x65, 0x28,
0x30, 0x39, 0xD4, 0x0C,
0x01, 0x2D, 0x25, 0x10,
0x09, 0x8F, 0x6A, 0x3F,
0x44, 0xD8, 0x6D, 0xC5,
0xA6, 0x72, 0x07, 0x83,
0x40, 0xC6, 0x8E, 0x1F,
0x77, 0x61, 0x96, 0x4A,
0x08, 0xFE, 0x53, 0x5A,
0xA1, 0xDF, 0xB6, 0x67,
0x66, 0x5C, 0x57, 0xB8,
0xD3, 0x11, 0x52, 0x21,
0xCC, 0x56, 0x2E, 0xC2,
0x88, 0xAA, 0xF9, 0x20,
0x7A, 0x6F, 0x4E, 0x76,
0xE8, 0xC1, 0xD5, 0xBD,
0xCE, 0x9E, 0x38, 0x95,
0x50, 0xF2, 0x9F, 0xB2,
0x9A, 0x0B, 0x47, 0x16,
0x60, 0xBF, 0xFD, 0x92,
0x35, 0x89, 0xDA, 0xFF,
0x9B, 0xBA, 0x13, 0xAB,
0xF4, 0x79, 0x87, 0xAC,
0x8C, 0x73, 0x84, 0xB3,
0x0E, 0xC8, 0x26, 0xA5,
0xE7, 0x15, 0xE9, 0xC3,
0x69, 0x70, 0xE0, 0x68,
0x42, 0x81, 0xCD, 0xEB,
0xDE, 0x7D, 0xEF, 0xD0,
0x24, 0x00, 0xF0, 0x41,
0xA0, 0xEE, 0x05, 0x94,
0x85, 0xBB, 0x43, 0x02,
0xF7, 0xC0, 0xD1, 0x1B,
0x7F, 0x5B, 0xEC, 0xF6,
0x2B, 0x1E, 0xE2, 0x27,
0xFB, 0x78, 0x54, 0x58,
0xE4, 0x32, 0xDB, 0xB7,
0xC7, 0x90, 0x7C, 0xF8,
0x5D, 0x5F, 0x63, 0xBE,
0x2C, 0x0A, 0xDD, 0x9C,
0x75, 0x19, 0xC4, 0xA8,
0x86, 0x36, 0xBC, 0x8D,
0xD7, 0x7B, 0xB4, 0x5E,
0x3E, 0xA7, 0xB1, 0xE1,
0x59, 0x82, 0xB9, 0xAE,
0xD9, 0x7E, 0xAF, 0xCF,
0x9D, 0xF5, 0xFA, 0x48,
0x4F, 0xA9, 0x6C, 0x64,
0x6E, 0x49, 0x4B, 0x6B,
0x29, 0x45, 0xE5, 0x04,
0xA4, 0x4C, 0x34, 0x80,
0xD2, 0x3D, 0xE3, 0x99,
0x37, 0xDC, 0x93, 0xC9,
0xCA, 0xCB, 0xEA, 0xB0,
0x0F, 0x03, 0x8A, 0xF3,
0x51, 0x1C, 0xAD, 0x74,
0x18, 0x2F
v4数组
v10的加密与解密
打开 sub_4018D0(v4, &v10, 8)函数
_DWORD *__cdecl sub_4018D0(_DWORD *a1, _BYTE *a2, int a3)
{
int v3; // edx
int v4; // ecx
int v5; // esi
_BYTE *v6; // ebx
int v7; // edi
unsigned int *v8; // eax
int v9; // edx
_DWORD *v10; // ebp
_DWORD *v11; // ST00_4
unsigned int v12; // ebp
_DWORD *result; // eax v3 = *a1;
v4 = a1[];
v5 = (int)(a1 + );
if ( a3 > )
{
v6 = a2;
v7 = *a1;
do
{
v7 = (unsigned __int8)(v7 + );
v8 = (unsigned int *)(v5 + * v7);
v9 = *v8;
v4 = (unsigned __int8)(*v8 + v4);
v10 = (_DWORD *)(v5 + * v4);
v11 = v10;
v12 = *v10;
*v8 = v12;
*v11 = v9;
*v6++ ^= *(_DWORD *)(v5 + * (unsigned __int8)(v9 + v12));
}
while ( v6 != &a2[a3] );
v3 = v7;
}
result = a1;
*a1 = v3;
a1[] = v4;
return result;
}
这里利用v4数组,对v10的每一位进行异或运算。
这个函数执行后,就是最后一个函数,刚刚我们暴力解出了61495072,v10 = '\x06\x01\x04\x09\x05\x00\x07\x02',因此我们能够对v10的每一位进行异或,得到v10加密前的值。
本来是准备利用提取出的v4,代入函数计算出v10的值,但是没成功,哈哈哈!后面发现v4的值实际上是固定的,不会受到输入的影响并且我只需要知道*v6异或的值即可。因此,我在OD中调试,记录异或的值。
0x7c, 0x0AB, 0x2D, 0x91, 0x2F, 0x98, 0x0ED, 0xA9
我们能够写出脚本
num = [0x7c, 0x0AB, 0x2D, 0x91, 0x2F, 0x98, 0x0ED, 0xA9]
v10 = '\x06\x01\x04\x09\x05\x00\x07\x02' flag = []
n = 0 for i in v10:
flag.append(hex(ord(i) ^ num[n]))
n = n + 1
print(flag)
['0x7a', '0xaa', '0x29', '0x98', '0x2a', '0x98', '0xea', '0xab']
组合起来,得到v9经过十六进制加密后的v10=0x7aaaa29982a98eaab
因此我们能够得到v9="7aaa29982a98eaab",输入到程序中得到flag。
get flag!
flag{5cb92582-66a8-e5b7-d3bf-3b99df8ac7f0}
2019 上海市大学生网络安全大赛 RE部分WP的更多相关文章
- 2019年上海市大学生网络安全大赛两道misc WriteUp
2019年全国大学生网络安全邀请赛暨第五届上海市大学生网络安全大赛 做出了两道Misc== 签到 题干 解题过程 题干提示一直注册成功,如果注册失败也许会出现flag. 下载下来是包含010edito ...
- Writeup:第五届上海市大学生网络安全大赛-Web
目录 Writeup:第五届上海市大学生网络安全大赛-Web 一.Decade 无参数函数RCE(./..) 二.Easysql 三.Babyt5 二次编码绕过strpos Description: ...
- 第三届上海市大学生网络安全大赛wp&学习
wp 0x00 p200 先分析了程序关键的数据结构 分析程序逻辑,在free堆块的时候没有清空指针,造成悬挂指针,并且程序中给了system('/bin/sh'),可以利用uaf 脚本如下: 1.先 ...
- 第三届上海市大学生网络安全大赛 流量分析 WriteUp
题目链接: https://pan.baidu.com/s/1Utfq8W-NS4AfI0xG-HqSbA 提取码: 9wqs 解题思路: 打开流量包后,按照协议进行分类,发现了存在以下几种协议类型: ...
- 2019全国大学生信息安全大赛两道web
简单小结 菜鸟第一次打国赛,这次题目质量很高,学到了许多姿势. Web Justsoso 打开题目,源代码出存在提示: 使用LFI读取index.php与hint.php http://d4dc224 ...
- 第十一届GPCT杯大学生程序设计大赛完美闭幕
刚刚过去的周六(6月7号)是今年高考的第一天,同一时候也是GPCT杯大学生程序设计大赛颁奖的日子,以下我们用图文再回想一下本次大赛颁奖的过程. 评审过程的一些花絮<感谢各位评审这些天的付出!&g ...
- [BFS,A*,k短路径] 2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛 path (Problem - 6705)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=6705 path Time Limit: 2000/2000 MS (Java/Others) Mem ...
- [贪心,dp] 2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛 Fishing Master (Problem - 6709)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=6709 Fishing Master Time Limit: 2000/1000 MS (Java/Othe ...
- 2020年第二届“网鼎杯”网络安全大赛 白虎组 部分题目Writeup
2020年第二届“网鼎杯”网络安全大赛 白虎组 部分题目Writeup 2020年网鼎杯白虎组赛题.zip下载 https://download.csdn.net/download/jameswhit ...
随机推荐
- MySQL的(@i:=@i+1)用处及用法
今天写一个为查询的数据排序列号的SQL语句,整理出来下面的笔记: 这是语法: SELECT (@i:=@i+1),t.* FROM table_name t,(SELECT @i:=0) AS j ...
- [BZOJ2729]:[HNOI2012]排队(组合数学)
题目传送门 题目描述 某中学有n名男同学,m名女同学和两名老师要排队参加体检.他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的) ...
- mysql 数据增删改的总结
一.在MySQL管理软件中,可以通过SQL语句中的DML语言来实现数据的操作,包括 1.使用INSERT实现数据的插入2.UPDATE实现数据的更新3.使用DELETE实现数据的删除4.使用SELEC ...
- 关于option标签的selected属性
当item的dict_id和custSource一样,那么当前的item的name就被选中并显示在页面 如果直接写selected="selected",就等于直接回显这个集合中最 ...
- laravel Route::resource() 资源路由
格式: Route::resource('/order', 'OrderController', ['as' => 'admin']); 框架自动创建路由及其对应控制器中的方法: 请求方式 路由 ...
- leetcode-mid-design-297. Serialize and Deserialize Binary Tree¶-NO -??
mycode 将list转换成树的时候没有思路 参考: deque 是双边队列(double-ended queue),具有队列和栈的性质,在 list 的基础上增加了移动.旋转和增删等 class ...
- 转 实例具体解释DJANGO的 SELECT_RELATED 和 PREFETCH_RELATED 函数对 QUERYSET 查询的优化(二)
https://blog.csdn.net/cugbabybear/article/details/38342793 这是本系列的第二篇,内容是 prefetch_related() 函数的用途.实现 ...
- kaptcha Spring 整合
转自:http://my.oschina.net/CandyDesire/blog/209364 生成验证码的方式有很多,个人认为较为灵活方便的是Kaptcha ,他是基于SimpleCaptcha的 ...
- kafka 通信报文格式
1. 序列化一条消息 消息有 key 和 value kafka 提供了基础数据类型的序列化工具,对于业务的自定义类需要自行实现序列化 ProducerRecord 是对象,含 KV 和 header ...
- 阶段3 1.Mybatis_07.Mybatis的连接池及事务_4 mybatis中使用unpooled配置连接池的原理分析
把之前的CRUD的代码src下的代码都复制过来 依赖项也都复制过来, 配置文件 整理一番 执行findAll方法的测试 查看日志的输出部分 修改程序池 再来执行findAll方法 Plooled从连接 ...