极客wp总结
队伍名:int 晓 队员:一打七QAQ Akuma 分数:4200
本wp全由一打七QAQ写完,一个开校真小白,真菜鸡,如果有什么问题和错误,请谅解。
官方wp:https://mp.weixin.qq.com/s/8R4lGzToBxjK1lC9B_SFBg
web题
1.朋友的学妹
打开网址,右键鼠标,打开源文件,发现
<!--flag=U1lDe0YxQF80c19oNExwZnVsbGxsbGx9-->
用网站base64解码得到flag
SYC{F1@_4s_h4Lpfullllll}
2.EZwww
打开网址,说这个网站已经备份了,直接在网址后面加上/www.zip,发现开始下载一个压缩包,解压,有两个文件,一个是假的flag,一个是index.php,把index.php的后缀改为txt。打开发现
<html>
<head>
<title>Lola's website1.0</title>
</head>
<body>
<?php echo '<h1>welcome to my website</h1>'; ?>
<?php echo '<p>i will never forget to backup my website......</p>'; ?>
<?php echo '<img src="img/lola.gif" alt="welcome~"/>'; ?>
</body>
</html>
<?php
$key1 = $_POST['a'];
$key2 = base64_decode('c3ljbDB2ZXI=');
if($key1 === $key2)
{
//this is a true flag
echo '<p>SYC{xxxxxxxxxxxxxxxxxx}</p>';
}
?>
说以post提交a,然后要key1等于key2,key2还base64编码了,解码得到sycl0ver,然后用火狐浏览器(注意要下载一个插件Hackbar)以post提交a=sycl0ver,得到flag SYC{Backup_1s_4_good_h4bit_l0l}
3.刘壮的黑页
这道题主要考post和get两个提交方式,不知道怎么回事,网站打不开了QAQ,反正就是用get方式提交一个东西,一个用post提交一个东西,就会得到flag
4.Welcome
打开网站,显示405错误,只不过不是题错了,而是要改用post提交一个东西才会显示一些东西,比如a=’1’,得到
<?php
error_reporting(0);
if ($_SERVER[‘REQUEST_METHOD’] !== ‘POST’) {
header(“HTTP/1.1 405 Method Not Allowed”);
exit();
} else {
if (!isset($_POST['roam1']) || !isset($_POST['roam2'])){
show_source(__FILE__);
}
else if ($_POST['roam1'] !== $_POST['roam2'] && sha1($_POST['roam1']) === sha1($_POST['roam2'])){
phpinfo(); // collect information from phpinfo!
}
}
再次以post提交roam1[]=1&roam2[]=2,一个绕过方式。会出现一个界面,从中找到一个可以位置 /f1444aagggg.php,在网址后面加上去,得到
Not Found
The requested URL was not found on this server.
Apache/2.4.38 (Debian) Server at 49.234.224.119 Port 8000
改为post请求发送,在消息头里面看到flag
SYC{w31c0m3_t0_5yc_r0@m_php1}
5.我是大黑客
这道题考的应该是一句话木马,打开网站,有提示说留下了一个备份文件,在网址后面加上/liuzhuang.php.bak。开始下载文件,把文件后缀名改为txt,打开,得到
<?php
eval($_POST['liuzhuang']);
//谁是大恶人 那必定是我liuzhuang
//当你的服务器看到 0xliuzhuang 就知道要买台新机器了
?>
用蚁剑
输入URL,密码为上面的liuzhuang。得到flag
SYC{1iuzHuang_yyd_G0d!}
6.ezbypass
Please use a GET request to pass in the variables a and b, compare them with strcmp and let strcmp return a value of NULL.
Note that a and b cannot be equal.
请使用GET请求传入变量a和b,将它们与strcmp进行比较,并让strcmp返回NULL值。
注意a和b不能相等。用strcmp函数的漏洞绕过
以get方式提交?a[ ]=2&b[ ]=4,然后得到
OKOK,You got the first step.
Please POST a variable c that is not a number to make it equal to 123
好吧,你迈出了第一步。请发布一个不是数字的变量c,使其等于123。开始我以为用ASCLL码就可以了,不行,用位运算使c=123,也不行。最后发现是php弱类型绕过。post提交c=123a,得到flag
nice!!!You got it
SYC{php_4s_so_funny}
re题目
1.No RE no gain
下载文件,直接拖到IDA里面,shift+f12查看字符串,有很多假的flag,例如
.rdata:0040400C 0000001F C SYC{5ay_He1lo_tH3_Re_W0rld!!!}
.rdata:0040402C 0000001F C SYC{Say_h3llo_th3_rE_World!!!}
.rdata:0040404C 0000001F C SYC{s4y_he110_The_RE_w0r1d!!!}
.rdata:0040406C 0000001F C SYC{S4y_He11o_th3_RE_World!!!}
.rdata:0040408C 0000001F C SYC{s4y_He110_The_rE_w0r1d~~~}
.rdata:004040AC 0000001F C SYC{say_h3ll0_Th3_RE_w0rld!!!}
.rdata:004040CC 0000001F C SYC{s4y_he110_th3_rE_w0rld~~~}
.rdata:004040EC 0000001F C SYC{s4y_he11o_The_r3_wor1d!!!}
.rdata:0040410C 0000001F C SYC{Say_He110_th3_r3_w0r1d!!!}
.rdata:0040412C 0000001F C SYC{say_He11o_The_rE_wor1d!!!}
.rdata:0040414C 0000001F C SYC{say_he11o_the_r3_Wor1d~~~}
.rdata:0040416C 0000001F C SYC{Say_He110_The_rE_w0r1d~~~}
.rdata:0040418C 0000001F C SYC{S4y_H3110_th3_RE_World!!~}
真正的flag在,.rdata:00404277 0000001A C Great! Here is your flag:,点进去,找到位置ctrl+x,到真正的flag处。得到
SYC{S4y_He11o_th3_RE_World!!!}
2.我真不会写驱动!
不知道是不是题有问题,直接拖到IDA里面,shift+f12查看字符串,就有了flag
SYC{First_Win64_DRIVER}
3.WhatsApk
APK实际上就是一个备份文件,解压后,得到一些文件,网上搜一搜apk文件结构,了解了解。
取巧做法将全部文件后缀名改为txt,然后一个一个文件的搜索SYC,得到flag
SYC{WelC0me_to_android!}
实际上估计应该是要用jed软件,然后在jeb里面搜索,下一道题就会用到。
4.HelloAndroid
首先安装jed,挺难找软件的,安好了还要安个Java。
安装好了,把class.dex文件直接拖到jeb里面,然后点字符串,搜索SYC,得到
SYC{Android_in_Java_is_easy!}
5.re00
考点:异或
要先到IDA里面看,shift+12,点进去可以的地方,ctrl+x,然后f5,看伪代码,关键点为
if ( (char)(buf[i] ^ 0x44) != byte_4060[i] )//buf[i]就是输入的flag,然后和0x44进行了一个异或运算,得到了byte_4060[i]。
点开byte_4060[i],其值为
byte_4060 db 17h, 1Dh, 7, 3Fh, 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh
.data:0000000000004060 ; DATA XREF: main+93↑o
.data:0000000000004060 db 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh, 3Ch, 2Bh, 3 dup(36h)
.data:0000000000004060 db 1Bh, 36h, 2Dh, 23h, 2Ch, 30h, 2 dup(7Bh), 39h
需要知道dup代表的是什么,然后异或回去就行
脚本如下
#include <stdio.h>
#include <stdlib.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
signed int i; // [rsp+8h] [rbp-48h]
int buf[32]; // [rsp+10h] [rbp-40h]
int byte_4060[32]={0x17,0x1D,0x07,0x3F,0x37,0x2D,0x29,0x34,0x28,0x21,0x1B,0x37,0x2D,0x29,0x34,0x28,0x21,0x1B,0x3C,0x2B,0x36,0x36,0x36,0x1B,0x36,0x2D,0x23,0x2C,0x30,0x7B,0x7B,0x39};
//从IDA中得到byte_4060的16进制数串 后面有一串字符 但是不像flag,发现if里面用异或运算编码了,写脚本来解码。
for ( i = 0; i <= 31; ++i )
{
buf[i]=byte_4060[i] ^0x44;//解码
}
for(i=0;i<=31;i++)
{
printf("%x ",buf[i]);//可改为printf("%c",buf[i]);直接得到字符串
}
printf("\n");
//得到解码后的16进制数串,将其转换为字符串,得到flag。
puts("yes! you get it!");
return 0LL;
}
得到flag
SYC{simple_simple_xorrr_right??}
6.maze
首先还是用IDA打开,shift+12搜索字符串,发现tttttttttttttttql!!!十分可疑,点进去,Ctrl+x进入主函数,F5生成伪代码,如下
int sub_401A10()
{
int v0; // ecx
int v1; // eax
int v2; // esi
char *v3; // ecx
unsigned int v4; // kr00_4
int v5; // eax
char v6; // cl
char v8[108]; // [esp+8h] [ebp-70h]
char v9[108]; // [esp+9h] [ebp-6Fh]
sub_402400(dword_431974);
sub_401CB0(v0, "give me your way:");
sub_406FD0(v8, 0, 100);
v1 = sub_409A97(0);
sub_409E54(v1);
sub_401100("%64s", (unsigned int)v8);
v2 = 0;
v3 = v9;
v4 = strlen(v8);
if ( v4 )
{
v5 = dword_431960;
while ( 1 )
{
v6 = v8[v2];
if ( v6 == 97 )
break;
switch ( v6 )
{
case 119:
v5 -= 31;
goto LABEL_11;
case 115:
v5 += 31;
goto LABEL_11;
case 100:
++v5;
goto LABEL_11;
}
LABEL_12:
LOBYTE(v3) = byte_42E820[v5];
if ( (_BYTE)v3 == 111 )
{
sub_401CB0(v3, "Ur not on the way!!");
sub_4099B9(0);
LABEL_17:
sub_401CB0(v3, "ttttttttttttttttttttql!!");
sub_4099B9(0);
JUMPOUT(*(_DWORD *)algn_401B14);
}
if ( (_BYTE)v3 == 69 )
goto LABEL_17;
if ( ++v2 >= v4 )
return 0;
}
--v5;
LABEL_11:
dword_431960 = v5;
goto LABEL_12;
}
return 0;
}
百度搜索:ctf 迷宫问题,https://wiki.x10sec.org/reverse/maze/maze/发现与这道题有相识之处,从而知道
.rdata:0042E821'__________ooooo_____o________o_ooooooooo__ooo__ooo_oooooooooo_ooo'
.rdata:0042E821 db 'ooooooo__o__oooo_oooooooooo________oooo___ooooo_ooooooooooooooooo'
.rdata:0042E821 db '_oooooooooooo_ooooooooooooooooo_ooooo_oooooo_ooooooooooooooooo_oo'
.rdata:0042E821 db 'ooo_oooooo_oooooooooos_______ooooo_oooooo__________Eooooooooooooo'
.rdata:0042E821 db 'oooooooooooooooooo',0
代表的是迷宫,用shift+e调出迷宫,发现迷宫数据,但是需要知道迷宫的长宽,我的做法是先调为16进制,通过拉扯边框的大小使里面数据成为一个矩形,发现变成9*31时恰好是个矩形!!!!,并且按照5F走迷宫的确有一条路。
再看主函数,发现
v5 -= 31;
v5 += 31;
中的31相当之可疑,发现这原来是1维数组变2维数组,每31个就换行。16进制的图十分不好看,换一下
#include<stdio.h>
int main(void)
{
int i,j;
int a[9][31]={ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
111, 111, 111, 111, 111, 95, 95, 95, 95, 95,
111, 95, 95, 95, 95, 95, 95, 95, 95, 111,
95, 111, 111, 111, 111, 111, 111, 111, 111, 111,
95, 95, 111, 111, 111, 95, 95, 111, 111, 111,
95, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 95, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 95, 95, 111, 95, 95, 111, 111, 111,
111, 95, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 95, 95, 95, 95, 95, 95, 95, 95,
111, 111, 111, 111, 95, 95, 95, 111, 111, 111,
111, 111, 95, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
95, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 95, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 95, 111, 111, 111, 111, 111, 95, 111, 111,
111, 111, 111, 111, 95, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 95, 111, 111, 111, 111, 111, 95, 111,
111, 111, 111, 111, 111, 95, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 115, 95, 95, 95,
95, 95, 95, 95, 111, 111, 111, 111, 111, 95,
111, 111, 111, 111, 111, 111, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 69, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111,0 };
for(i=0;i<9;i++)
{
for(j=0;j<31;j++)
{
printf("%c",a[i][j]);
}
printf("\n");
}
}
发现运行后不对
__________ooooo_____o________o_
ooooooooo__ooo__ooo_oooooooooo_
oooooooooo__o__oooo_oooooooooo_
_______oooo___ooooo_ooooooooooo
oooooo_oooooooooooo_ooooooooooo
oooooo_ooooo_oooooo_ooooooooooo
oooooo_ooooo_oooooo_oooooooooos
_______ooooo_oooooo__________Eo
oooooooooooooooooooooooooooooo
--------------------------------
Process exited after 0.1652 seconds with return value 0
请按任意键继续. . .
迷宫怎么会缺了一个角。观察地图,最右边那一列如果移到左边不就感觉更好了,感觉应该是少了一个,添上去,c代码如下
#include<stdio.h>
int main(void)
{
int i,j;
int a[9][31]={ 95,95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
111, 111, 111, 111, 111, 95, 95, 95, 95, 95,
111, 95, 95, 95, 95, 95, 95, 95, 95, 111,
95, 111, 111, 111, 111, 111, 111, 111, 111, 111,
95, 95, 111, 111, 111, 95, 95, 111, 111, 111,
95, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 95, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 95, 95, 111, 95, 95, 111, 111, 111,
111, 95, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 95, 95, 95, 95, 95, 95, 95, 95,
111, 111, 111, 111, 95, 95, 95, 111, 111, 111,
111, 111, 95, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
95, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 95, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 95, 111, 111, 111, 111, 111, 95, 111, 111,
111, 111, 111, 111, 95, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 95, 111, 111, 111, 111, 111, 95, 111,
111, 111, 111, 111, 111, 95, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 115, 95, 95, 95,
95, 95, 95, 95, 111, 111, 111, 111, 111, 95,
111, 111, 111, 111, 111, 111, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 69, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111, 111, 111,
111, 111, 111, 111, 111, 111, 111, 111,};
for(i=0;i<9;i++)
{
for(j=0;j<31;j++)
{
printf("%c",a[i][j]);
}
printf("\n");
}
}//(注意要把数组中最后的0去掉)
运行
___________ooooo_____o________o
_ooooooooo__ooo__ooo_oooooooooo
_oooooooooo__o__oooo_oooooooooo
________oooo___ooooo_oooooooooo
ooooooo_oooooooooooo_oooooooooo
ooooooo_ooooo_oooooo_oooooooooo
ooooooo_ooooo_oooooo_oooooooooo
s_______ooooo_oooooo__________E
ooooooooooooooooooooooooooooooo
--------------------------------
Process exited after 0.07231 seconds with return value 0
请按任意键继续. . .
迷宫准备就绪。感觉出题人相当有意思,SYC;迷宫中的s应该代表的是start起点,E应该代表的是end终点。实际上看主函数也可看出终点
LABEL_17:
sub_401CB0(v3, "ttttttttttttttttttttql!!");
sub_4099B9(0);
JUMPOUT(*(_DWORD *)algn_401B14);
}
if ( (_BYTE)v3 == 69 )//如果v3=69,也就是E,跳到LABEL_17,说ttttttttttttttql!!!说明是正确的终点
goto LABEL_17;
现在需要做的就是走迷宫,迷宫肯定需要方向,是什么呢?再看主函数
v6 = v8[v2];
if ( v6 == 97 )//‘a’=97,--v5不就代表向左移动吗
--v5;//做了一点改动
switch ( v6 )
{
case 119://'199'=w,v5 -= 31不就是向上移动吗
v5 -= 31;
goto LABEL_11;
case 115://‘115’=s, v5 += 31不就是向下移动吗
v5 += 31;
goto LABEL_11;
case 100://'100'=d,++5不就表示向右移动吗
++v5;
goto LABEL_11;
}
感觉出题人,非常人性化,知道打游戏这几个键最常用QAQ。tqltqltql
开始走迷宫,得到flag(注意s和E不算)
得到flag:SYC{dddddddwwwwaaaaaaawwwddddddddddsdsdsddwdwdwddddsssssssdddddddddd}
7.Hello .NET
一.反编译
文件下载下来是个可执行文件,需要反编译来看代码
首先下载反编译软件
https://www.cnblogs.com/heibai-ma/p/12893654.html
二.分析代码
反编译后得到的关键代码如下(需要自己去在反编译出来的文件里面找找)
public class MainWindow : Window, IComponentConnector
{
internal TextBox InputBox;
internal Button Check_Btn;
internal TextBlock Status;
private bool _contentLoaded;
public MainWindow()
{
this.InitializeComponent();
}
private void Check(object sender, RoutedEventArgs e)
{
int num;
string text = this.InputBox.Text; //输入一个flag,也是要求的目标
List<int> list = new List<int>(); // https://zhidao.baidu.com/question/539039145.html
int[] numArray = new int[] {
0x12, 14, 40, -14, -2, 30, 10, 0x2a, 0x23, 0x30, 0x2b, 0x31, 0x34, 0x48, 0x39, 0x44,
0x56, 0x91, 0x73, 0x80, 0x73, 0x56
};//长度为22
for (num = 0x63; list.Count < text.Length; num += 2) //0x63=99, list.Count < text.Length,意思是要list[num]这个数组的长度和list.text[]数组长度一样,实际上list[num],text[num],numArray[num]的长度都要一样,都为22.
{
bool flag2 = true; //布尔变量
for (int i = 3; i < num; i += 2)//这个循环就是为了求list[num]这个数组。和求素数基本差不多,有点不一样。
{
if ((num % i) == 0)
{
flag2 = false;
break;
}
}
if (flag2)
{
list.Add(num); // 得到list[num]这个下面要用的数组
}
}
bool flag = true;
for (num = 0; (num < text.Length) && (num < numArray.Length); num++)
{
if ((list[num] - text[num]) != numArray[num]) // 不等于的话,flag=false,就说明错了,说明等于的时候才是对的,意思是flag text[num]=list[num]-numArray[num]
{
flag = false;
break;
}
}
if ((text.Length == numArray.Length) & flag)
{
this.Status.Foreground = new SolidColorBrush(Colors.Green);
this.Status.Text = "Flag is corrent";
}
else
{
this.Status.Foreground = new SolidColorBrush(Colors.Red);
this.Status.Text = "Flag is incorrent";
}
}
三.写脚本
脚本1
#include<stdio.h>
int main(void)
{
int num;
char a[22]={0,};
int b[22]={18,14,40,-14,-2,30,10,42,35,48,43,49,52,72,57,68,86,145,115,128,115,86};
int c[22]={0,};
int len=100;
int flag2,i,j,n;
for(num=99,j=0;j<60;num+=2,j++)
{
n=0;
flag2=1;
for(i=3;i<num;i+=2)
{
if(num%i==0)
{
flag2=0;
break;
}
}
if(flag2)
{ c[n]=num;
printf("%d ",c[n]);
n++;//得到c[]={101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211}
}
// b[]={18,14,40,-14,-2,30,10,42,35,48,43,49,52,72,57,68,86,145,115,128,115,86};
}
// printf("\n");
// for(num=0;num<22;num++)
// {
// a[num]=c[num]-b[num];
// printf("%d ",a[num]);
// }
//
//
}
脚本2
#include<stdio.h>
int main(void)
{
int a[22]={18,14,40,-14,-2,30,10,42,35,48,43,49,52,72,57,68,86,145,115,128,115,86};
int b[22]={101 ,103 ,107 ,109 ,113 ,127 ,131 ,137 ,139 ,149 ,151 ,157 ,163, 167 ,173 ,179 ,181 ,191 ,193 ,197 ,199 ,211};
int c[22]={0};
int i;
for(i=0;i<22;i++)
{
c[i]=b[i]-a[i];
printf("%c",c[i]);
}
}
得到flag
SYC{say_hello_to_.NET}
8.刘壮的BaseXX
一.调试与代码分析
拿到exe文件,直接拖到IDA里面看伪代码,如下
__int64 sub_1400014E0()
{
void *v1; // [rsp+28h] [rbp-B0h]//定义了一个数组
signed __int64 v2; // [rsp+30h] [rbp-A8h] //一个整型变量,应该是数组的长度
char v3[128]; // [rsp+40h] [rbp-98h]//又定义了一个数组
memset(v3, 0, sizeof(v3));
v1 = (void *)sub_140004EF4(256i64);
sub_140003498(aYouMightKnowBa);
while ( 1 )
{
while ( 1 )
{
sub_140001060((__int64)aInputYourFlag);//叫你输入flag
sub_140004ED8((__int64)v3, 128i64);//将flag赋给v3
v2 = -1i64;
do
++v2;
while ( v3[v2] );//应该算v2的值,计算数组长度
sub_140001420((__int64)v3, v2);//第一步加密,异或和位运算,需要进一步看子函数。
memset(v1, 0, 0x100ui64);
sub_1400010E0((__int64)v3, (__int64)v1, v2);//改了base64编码索引表的加密方式,将v3再次加密,需要进一步看子函数。
if ( v1 )
break;
sub_140003498(aUnExpectedErro);
}
if ( (unsigned int)sub_140001490((unsigned __int8 *)v1) )//将最后的v1和设定的字符串进行比较,如果对了就会跳出循环,说明正确,设定的字符串为 maj7TmztjquUN8Xm-hKplvaYfEAxrUnIc51qxlKOwCKN4XsdzBmjOd_- ,长度为56,也可通过这个来计算v3长度为42。
break;
sub_140001060((__int64)aWrongFlagTryAg);
}
sub_140001060((__int64)aCorrectYouAreR);
sub_140004EE0(v1);
sub_140004C24(aPause);
return 0i64;
}
重要子函数1
__int64 __fastcall sub_140001420(__int64 a1, unsigned int a2)//a1是v3,也就是加密后的,v2是数组长度。
{
__int64 result; // rax
signed int i; // [rsp+4h] [rbp-14h]
for ( i = 0; ; ++i )
{
result = a2;
if ( i >= (signed int)a2 )
break;
*(_BYTE *)(a1 + i) = i ^ ((*(_BYTE *)(a1 + i) >> 4) | 16 * *(_BYTE *)(a1 + i));//加密方式,注意其中的16*,意思是<<4,如果是8*的话,是<<3。
}
return result;
}
重要子函数2,整体的加密过程不做过多分析
__int64 __fastcall sub_1400010E0(__int64 a1, __int64 a2, int a3)//a1是v3,是加密前的,a2是v2是加密后的,a3是原来的长度。
{
int v3; // ST00_4
int v4; // ST00_4
int v5; // ST00_4
int v7; // [rsp+0h] [rbp-18h]
int v8; // [rsp+4h] [rbp-14h]
int v9; // [rsp+8h] [rbp-10h]
v9 = a3 % 3;
v8 = 0;
v7 = 0;
while ( v8 < a3 - v9 )
{
*(_BYTE *)(a2 + v7) = aZyxwvutsrqponm[(signed int)*(unsigned __int8 *)(a1 + v8) >> 2];
v3 = v7 + 1;
*(_BYTE *)(a2 + v3++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 1) & 0xF0) >> 4) | 16 * (*(_BYTE *)(a1 + v8) & 3)];
*(_BYTE *)(a2 + v3++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 2) & 0xC0) >> 6) | 4
* (*(_BYTE *)(a1 + v8 + 1) & 0xF)]; //4*是<<2。
*(_BYTE *)(a2 + v3) = aZyxwvutsrqponm[*(_BYTE *)(a1 + v8 + 2) & 0x3F];
v7 = v3 + 1;
v8 += 3;
}
if ( v9 == 1 )
{
*(_BYTE *)(a2 + v7) = aZyxwvutsrqponm[(signed int)*(unsigned __int8 *)(a1 + v8) >> 2];
v4 = v7 + 1;
*(_BYTE *)(a2 + v4++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 1) & 0xF0) >> 4) | 16 * (*(_BYTE *)(a1 + v8) & 3)];
*(_BYTE *)(a2 + v4++) = 61;
*(_BYTE *)(a2 + v4) = 61;//两个61代表加两个==
v7 = v4 + 1;
}
else if ( v9 == 2 )
{
*(_BYTE *)(a2 + v7) = aZyxwvutsrqponm[(signed int)*(unsigned __int8 *)(a1 + v8) >> 2];
v5 = v7 + 1;
*(_BYTE *)(a2 + v5++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 1) & 0xF0) >> 4) | 16 * (*(_BYTE *)(a1 + v8) & 3)];
*(_BYTE *)(a2 + v5++) = aZyxwvutsrqponm[((*(_BYTE *)(a1 + v8 + 2) & 0xC0) >> 6) | 4
* (*(_BYTE *)(a1 + v8 + 1) & 0xF)];
*(_BYTE *)(a2 + v5) = 61;
v7 = v5 + 1;
}
return (unsigned int)v7;
}
其中最关键的还是那个 zyxwvutsrqponmlkjihgfedcbaABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-_ ,这是将编码的索引表改了。
// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE *v4; // [rsp+20h] [rbp-30h]
int v5; // [rsp+2Ch] [rbp-24h]
int v6; // [rsp+44h] [rbp-Ch]
int v7; // [rsp+48h] [rbp-8h]
int i; // [rsp+48h] [rbp-8h]
int v9; // [rsp+4Ch] [rbp-4h]
_main(*(_QWORD *)&argc, argv, envp);
v5 = strlen("Happy birthday! yangtianci!");
if ( v5 % 3 )
v9 = 4 * (v5 / 3 + 1);
else
v9 = 4 * (v5 / 3);
v4 = malloc(v9 + 1i64);
v4[v9] = 0;
v7 = 0;
v6 = 0;
while ( v7 < v5 )
{
v4[v6] = aAbcdefghijklmn[(char)(aHappyBirthdayY[v7] >> 2)];
v4[v6 + 1] = aAbcdefghijklmn[16 * (aHappyBirthdayY[v7] & 3) | (char)(aHappyBirthdayY[v7 + 1] >> 4)];
v4[v6 + 2] = aAbcdefghijklmn[4 * (aHappyBirthdayY[v7 + 1] & 0xF) | (char)(aHappyBirthdayY[v7 + 2] >> 6)];
v4[v6 + 3] = aAbcdefghijklmn[aHappyBirthdayY[v7 + 2] & 0x3F];
v7 += 3;
v6 += 4;
}
if ( v5 % 3 == 1 )
{
v4[v9 - 2] = 61;
v4[v9 - 1] = 61;
}
else if ( v5 % 3 == 2 )
{
v4[v9 - 1] = 61;
}
for ( i = 0; i < v9; ++i )
putchar((unsigned __int8)v4[i]);
return 0;
}
可以将自己写的base编码放到IDA里面看,与题中对比,大同小异。
总结:看伪代码需要明确知道定义的各个变量的含义,以及放到子函数总哪个变量对应哪个变量,慢慢分析。
写脚本
写脚本需要掌握base64解码
#include <stdio.h>
#include <stdlib.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
char *str1="maj7TmztjquUN8Xm-hKplvaYfEAxrUnIc51qxlKOwCKN4XsdzBmjOd_-";
unsigned char *str2;
int table[]={0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,62,0,
0,61,60,59,58,57,56,55,
54,53,52,0,0,0,0,0,0,0,26,
27,28,29,30,31,32,33,34,35,36,37,38,
39,40,41,42,43,44,45,46,47,
48,49,50,51,0,0,0,0,63,0,25,
24,23,22,21,20,19,18,17,16,
15,14,13,12,11,10,9,8,7,
6,5,4,3,2,1,0
}; //换了之后的索引表
int len1;
int len2;
int i,j;
len1=strlen(str1);
if(strstr(str1,"=="))
{
len2=(len1/4)*3-2;
}
else if(strstr(str1,"="))
{
len2=(len1/4)*3-1;
}
else
{
len2=(len1/4)*3;
}
str2=malloc(sizeof(unsigned char)*len2+1);
str2[len2]='\0';
for(i=0,j=0;i<len1-2;i+=4,j+=3)
{
str2[j]=((table[str1[i]])<<2)|((table[str1[i+1]])>>4);
str2[j+1]=(table[str1[i+1]]<<4)|((table[str1[i+2]])>>2);
str2[j+2]=((table[str1[i+2]])<<6)|(table[str1[i+3]]);
}
// printf("%d",len2);
for(i=0;i<len2;i++)
{
printf("%d ",str2[i]);
}
printf("\n") ;
//第一步base解码
for(i=0;i<len2;i++)
{
str2[i]=((str2[i]^i)<<4)|((str2[i]^i)>>4); //第二步解密。
printf("%c",str2[i]);
}
return 0;
}
三.得到flag
SYC{M0dified_B@se64_is_Sti11_S1mpl3_Right}
9.un_snake
一.反汇编
首先下载下来发现是pyc的文件,网上一搜需要反编译,用网上在线编译,发现少了很多代码。然后用uncompyle6进行反编译
uncompyle6 -o C:\Users\hp\Downloads\un_snake.cpython-38.py C:\Users\hp\Downloads\un_snake.cpython-38.pyc
在cmd中输入这串代码如果这样有
# Successfully decompiled file
代表成功了
二.分析代码
将反编译出来的.py文件后缀名改为.c。为了好看一点,打开如下
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
# Embedded file name: ./un_snake.py
# Compiled at: 2020-08-05 16:20:40
# Size of source mod 2**32: 1238 bytes
import this
from base64 import *
def pre(data):
th1s = 'TBESCFSRSAEUITANAIIN'.encode()
if (data_len := len(data)) > (th1s_len := len(th1s)):
th1s = th1s * (data_len // th1s_len) + th1s[:data_len - th1s_len] #这个if主要是将date的长度弄的和th1s一样
return bytes(map(lambda x, y: x ^ y, data, th1s)) #再算stuff.encode()
def enc(plain):
plain = list(plain)
plain = plain[::-1] #将字符串反着排序
for i in range(len(plain)):
c = plain[i]
plain[i] = (c << 3 | c >> 5) & 255 #先执行for循环,再执行else里面的
else:
for i in range(len(plain) - 1):
plain[i] ^= plain[(i + 1)]#异或运算,将plain加密了
else:
return bytes(plain) #再算stuff_ready
def check(a):
return b64encode(a) == b'mEiQCAjJoXJy2NiZQGGQyRm6IgHYQZAICKgowHHo4Dg=' //base64解码,先算enc(stuff_ready)
if __name__ == '__main__':
print()
while True:
stuff = input('Now input you flag:') //输入的flag,最终目标
stuff_ready = pre(stuff.encode())
result = check(enc(stuff_ready))
#其实就是3个逆向的问题,一步一步往上推,每一个函数都代表一个加密,其中第2个加密最复杂
if result:
print('You get it! Python is so charming right?')
break
else:
print('Failed, try again!')
print('[馃悕] Commit you flag, see you next time!')
看到这个Python代码,开始什么感觉有些地方看不懂,网上查了一部分,Python自学了一部分(也就学到了列表),然后进行分析个大概思路,简略分析在上面的代码后面,下面是查找的一些内容。
1.plain = plain[::-1]
b = a[i:j] 表示复制a[i]到a[j-1],以生成新的list对象
a = [0,1,2,3,4,5,6,7,8,9]
b = a[1:3] # [1,2]
当i缺省时,默认为0,即 a[:3]相当于 a[0:3]
当j缺省时,默认为len(alist), 即a[1:]相当于a[1:10]
当i,j都缺省时,a[:]就相当于完整复制一份a
b = a[i:j:s]表示:i,j与上面的一样,但s表示步进,缺省为1.
所以a[i:j:1]相当于a[i:j]
当s<0时,i缺省时,默认为-1. j缺省时,默认为-len(a)-1
所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍,即倒序。
2.map(lambda x, y: x ^ y, data, th1s)
https://blog.csdn.net/landing_guy_/article/details/109331685
3.for循环后面直接接else(主要是学Python没见过,不知道怎么走)
https://blog.csdn.net/wj1066/article/details/81913696
三.写脚本
然后开始写脚本,由于本人很菜所以,将base64解码分开写的。
由于网上直接base64解码,会出现乱码,所以在Python中解码,代码如下
import base64
url = "mEiQCAjJoXJy2NiZQGGQyRm6IgHYQZAICKgowHHo4Dg="
str_url = base64.b64decode(url)
print(str_url)
得到结果
b'\x98H\x90\x08\x08\xc9\xa1rr\xd8\xd8\x99@a\x90\xc9\x19\xba"\x01\xd8A\x90\x08\x08\xa8(\xc0q\xe8\xe08'
是16进制和一些字符的组合,全部换为10进制(因为我比较喜欢10进制),注意最后的那个8,它转换为10进制是56,就是这一个数,会让你得不到flag(本人深有体会)。转出来如下
a[]={152,72,144,8,8,201,161,114,114,216,216,153,64,97,144,201,25,186,34,1,216,65,144,8,8,168,40,192,113,232,224,56}
然后进行第二,三步解密
#include<stdio.h>
#include <string.h>
int main(void)
{
int a[]={152,72,144,8,8,201,161,114,114,216,216,153,64,97,144,201,25,186,34,1,216,65,144,8,8,168,40,192,113,232,224,56};//base64解码后的10进制。
int b[]={84,66,69,83,67,70,83,82,83,65,69,85,73,84,65,78,65,73,73,78,84,66,69,83,67,70,83,82,83,65,69,85};//这是上面Python中的th1s换成了10进制,可以另外写一个脚本吧上面th1s用整型输出,就不用一个一个写。因为base64解码后的长度为64,故b[]的长度也必须要是32,也就是原来的基础上加上前12个字符。
int i,j;
int c,t,len;
len=sizeof(a)/sizeof(int);//求出解码后的长度,32
printf("%d",len);
printf("\n");
for(i=0;i!=len;i++)
{
printf("%d,",a[i]);
}
printf("\n");
printf("%d",a[31]);
printf("\n");
for(i=len-2;i!=-1;i--)
{
a[i]=a[i]^a[i+1];//反向异或回去,可以自己举4个数来看看是不是真的可以反向异或回去。
}
for(i=0;i!=len;i++)
{
printf("%d,",a[i]);
}
printf("\n");
for(i=0;i!=len;i++)
{
a[i]=((a[i]<<5)|(a[i]>>3))&255;//进行位移运算,也可以举例看看怎么弄回去。注意的是其中的&,作用为:主要是限定范围。不会得到大于255的数。
printf("%d,",a[i]);
}//输出位移后的数组
printf("\n");
for(i = 0; i < (len-1)/2; i++)
{
t = a[i];
a[i] = a[len-i-1];
a[len-i-1] = t;
}//将字符串倒序
for(i=0;i!=len;i++)
{
printf("%d,",a[i]);
}
printf("\n");
for(i=0;i!=len;i++)
{
a[i]=a[i]^b[i];
printf("%c",a[i]);
}//输出最后的flag
附加脚本
16进制转10进制
#include <stdio.h>
int main(void)
{
int i;
int a[]={0x98,0x48,0x90,0x08,0x08,0xc9,0xa1,0x72,0x72,0xd8,0xd8,0x99,0x40,0x61,0x90,0xc9,0x19,0xba,0x22,0x01,0xd8,0x41,0x90,0x08,0x08,0xa8,0x28,0xc0,0x71,0xe8,0xe0,0x38};
for(i=0;i!=32;i++)
{
printf("%d,",a[i]);
}
printf("\n");
return 0;
}
th1s转10进制
#include<stdio.h>
int main(void)
{
char a[]="TBESCFSRSAEUITANAIINTBESCFSRSAEU";
int i;
for(i=0;a[i]!='\0';i++)
{
printf("%d,",a[i]);
}
return 0;
}
四.得到flag
SYC{ssssss_Th1sfPs_Pyth0n_ssss~}#我得到的是这个,可能上面某个位置数据错了,看了下flag,直接改一下得到下面的flag。
SYC{ssssss_Th1s_is_Pyth0n_ssss~}
Misc题目
1.一“页”障目
直接把宣传单对折,得到flag
2.壮言壮语
直接网上佛曰解密,得到flag
我刘壮就是np,给你flag吧,SYC{i_l0ve_Japanese_wife}
3.秘技·反复横跳
网上根据提示搜索binwalk。
https://www.jianshu.com/p/03f15e6fedf3
一道类似的题,在ubuntu中执行网站中的操作就可以了,最后分解图片会得到一个二维码,但是是左右交换了的,也算点题把,交换回来,微信扫一扫,就可以得到flag了
SYC{L3ft~Le4t~R1gHt~RiGhT}
4.飞翔的刘壮
题目提示: Welcome “into” 11th Geek Challeng.这里的into就应该代表的是10。
一个游戏,下载到手机上,在得到10分的时候死了,就可以得到flag
SYC{fl4ppyb1rd_1s_s0_inter3tin9!}
5.来拼图
就是从众多图片中选出有flag信息的拼在一起,拼出来有点不好看
SYC{d60fca51c7e61259191f0}
6.吉普赛的歌姬
不知道这道题考什么,考信息收集能力?去QQ空间看看,然后知道flag在相册里面,然后根据说说前两条,先到贴吧看看,然后搜索DJ南方,然后叫你去网易找伪·Nightcore这个电台,(中间还会遇到两个base64加密的假flag,人心险恶)找一首叫吉普赛歌姬的歌(歌里面什么也没有),但是歌下面有评论说,密码是名字+生日,然后进入相册,得到一张图片,上面有flag
SYC{Fr1endsh1p_1s_w1tchcr@ft}
pwn题目
1.数学咋样?
用ubuntu打开端口
luckyboy@luckyboy-virtual-machine:~$ nc 81.69.0.47 1111
------------------------------------------
Can you help me to solve my math problem?
------------------------------------------
I have 20 tests
![0] num_1 = 822, num_2 = 701
I can't calculate the expression 'num_1 + num_2'.
input your answer:
进行20次计算,最后得到flag
2.runcode
考点是读取一个文件并输出,百度搜索一下怎么读取一个文件
https://blog.csdn.net/qq_26853817/article/details/79756617,根据这个来写脚本。如下
#include <stdio.h>
int main(void)
{
char a[200] = {0};
FILE *fp = fopen("/home/ctf/flag", "r");
fgets(a, 100, fp);
puts(a);
return 0;
}
注意数组长度要足够长,不然读不完。放到网站里面run,run两次,得到flag
SYC{C0din9_ls_E4sy_T0_You}
密码学题目
1.二战情报员刘壮
摩氏密码,直接网上在线解密,得到L1UZHU4NG_I_Z1Y1,得到flag
SYC{L1UZHU4NG_I_Z1Y1}
2.铠甲与萨满
凯撒密码,直接网上在线解密,先根据前三个字母确定位移为6,也可以一个一个试,解密出来的flag为
SYC{liuzhuangliuzhuang_bangbangbang}
3.跳跃的指尖
跳跃的指尖,需要脑洞大一点,就是键盘啦,每一段字母都会围一个单词,写出所有单词就是flag
SYC{easyCrypto}
4.成都养猪二厂
猪圈密码和栅栏密码,网上在线解密,先把图片中的猪圈密码解出来得到ssyirceehsagiulisolbhy,然后根据那个txt里面的内容确定栅栏密码的位移量为7,网上在线解密,得到flag
SYC{his_house_is_really_big}
5.规规矩矩的工作
看题目和提示,应该是考矩阵和希尔密码,直接在网上解矩阵,https://matrix.reshish.com/,得到
x1 = 10
x2 = 4
x3 = -2
A=0,B=1,C=2……然后换为字母,为KEY,用cmd打开decode_machine.exe(要不然直接打开好像会闪退),输入KEY,得到flag
SYC{linear_algebra_make_ctf_great_again}
6.Simple calculation
这道题部分靠运气,直接解矩阵,得到
x1 = 77/2
x2 = -48
x3 = -21
x4 = 65
x5 = -63/2
有分数,x1和x5分数求余的话好像网上找了很多也没有解出来,然后试着将,3 7 1 1 20换为29 7 1 1 -6(实际上试了很多数,加上一点点LOL基础),解出来得到
x1 = 19
x2 = 30
x3 = 18
x4 = -65
x5 = 27
全部换到0~26范围,得到flag
SYC{TESNB}
7.韡髻猊岈
Vigenere解密,网上自己找资料,了解原理,Vigenere解密第一步要确定密钥长度,第二步要算出密钥是什么,这个要知道原理和对照那个Vigenere表来算密钥。
首先根据相同的单词算出密钥长度为6,在根据加密内容中的TGP(解密出来肯定是SYC),算出半个密钥为bin,然后开始在bin前面加aaa变成aaabin,去解密,发现SYC后面的英文为
vhxre iu nh teats bn viiegere,
那个iu,肯定是is,通过这个又算出一个密钥的字母caabin,在用这个来解密,得到
thxre is nh tears bn vigegere
这个thxre肯定是there,从而算出密钥为catbin,得到flag
SYC{there_is_no_tears_in_vigenere}