Skip to content

Commit d1a2c5d

Browse files
committed
fix: 修复randomAgent参数不生效的问题
- 修复Task初始化时过早调用apply_header_rules()导致randomAgent参数未生效的问题 - 在apply_header_rules()中正确处理randomAgent参数,移除原始User-Agent头 - 在_build_raw_http_request()中跳过原始User-Agent头 - 确保SQLMap能正确添加随机User-Agent
1 parent 9356e1b commit d1a2c5d

6 files changed

Lines changed: 137 additions & 29 deletions

File tree

src/backEnd/doc/sqlmap_cmd_help_doc.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
___
22
__H__
3-
___ ___[']_____ ___ ___ {1.9.11.3#dev}
4-
|_ -| . [)] | .'| . |
3+
___ ___["]_____ ___ ___ {1.9.11.3#dev}
4+
|_ -| . [,] | .'| . |
55
|___|_ [']_|_|_|__,| _|
66
|_|V... |_| https://sqlmap.org
77

src/backEnd/model/Task.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ def __init__(self, taskid, remote_addr, scanUrl, host, method, headers, body):
7070

7171
logger.debug(f"[{self.taskid}] Task initialized with {len(self.headers) if self.headers else 0} headers")
7272

73-
# 在任务创建时就处理请求头,确保请求头规则立即生效
74-
self.apply_header_rules()
73+
# 注意:不在这里调用 apply_header_rules()
74+
# 因为此时 randomAgent 等参数还未设置
75+
# apply_header_rules() 会在 engine_start() 中被调用
7576

7677
logger.debug(f"[{self.taskid}] Task initialization completed")
7778

@@ -161,13 +162,26 @@ def apply_header_rules(self):
161162
# 更新请求头
162163
if processed_headers != self.headers:
163164
self.headers = processed_headers
164-
# 将处理后的请求头设置到SQLMap配置中
165-
# SQLMap期望headers是一个换行符分隔的字符串
166-
self.options.headers = "\n".join(processed_headers)
165+
166+
# 如果启用了randomAgent,从headers中移除User-Agent
167+
# 这样SQLMap的_setHTTPUserAgent()会添加随机UA
168+
logger.debug(f"[{self.taskid}] Checking randomAgent: {self.options.randomAgent}")
169+
if self.options.randomAgent:
170+
original_count = len(self.headers)
171+
self.headers = [h for h in self.headers
172+
if not h.lower().startswith("user-agent:")]
173+
if len(self.headers) < original_count:
174+
logger.info(f"[{self.taskid}] Removed User-Agent from headers due to randomAgent=True")
175+
else:
176+
logger.debug(f"[{self.taskid}] No User-Agent header found to remove")
177+
178+
# 将请求头设置到SQLMap配置中
179+
# SQLMap期望headers是一个换行符分隔的字符串
180+
self.options.headers = "\n".join(self.headers)
181+
182+
if applied_rules:
167183
logger.info(f"[{self.taskid}] Applied {len(applied_rules)} header rules: {', '.join(applied_rules)}")
168-
logger.debug(f"[{self.taskid}] Set headers option for SQLMap: {self.options.headers}")
169-
else:
170-
logger.debug(f"[{self.taskid}] No header changes applied")
184+
logger.debug(f"[{self.taskid}] Set headers option for SQLMap: {self.options.headers}")
171185

172186
# 标记请求头规则已应用
173187
self._header_rules_applied = True
@@ -284,6 +298,11 @@ def _build_raw_http_request(self):
284298
if self.headers:
285299
for header in self.headers:
286300
if header and ":" in header:
301+
# 如果启用了randomAgent,跳过原始User-Agent头
302+
# 让SQLMap的_setHTTPUserAgent()添加随机UA
303+
if self.options.randomAgent and header.lower().startswith("user-agent:"):
304+
logger.debug(f"[{self.taskid}] Skipping original User-Agent header due to randomAgent=True")
305+
continue
287306
headers_list.append(header)
288307

289308
# 确保Host头存在

src/burpEx/legacy-api/src/main/java/com/sqlmapwebui/burp/ScanConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ public Map<String, Object> toOptionsMap() {
11401140
// Enumeration Extended
11411141
if (getAll) options.put("getAll", true);
11421142
if (getHostname) options.put("getHostname", true);
1143-
if (getPasswords) options.put("getPasswords", true);
1143+
if (getPasswords) options.put("getPasswordHashes", true);
11441144
if (getPrivileges) options.put("getPrivileges", true);
11451145
if (getRoles) options.put("getRoles", true);
11461146
if (getSchema) options.put("getSchema", true);

src/burpEx/montoya-api/src/main/java/com/sqlmapwebui/burp/ScanConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ public Map<String, Object> toOptionsMap() {
11401140
// Enumeration Extended
11411141
if (getAll) options.put("getAll", true);
11421142
if (getHostname) options.put("getHostname", true);
1143-
if (getPasswords) options.put("getPasswords", true);
1143+
if (getPasswords) options.put("getPasswordHashes", true);
11441144
if (getPrivileges) options.put("getPrivileges", true);
11451145
if (getRoles) options.put("getRoles", true);
11461146
if (getSchema) options.put("getSchema", true);

src/frontEnd/src/utils/paramDefinitions.ts

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,27 +97,56 @@ export const PARAM_DEFINITIONS: ParamDefinition[] = [
9797
{ key: 'threads', name: 'threads', cliName: '--threads', description: '并发线程数', category: 'optimization', type: 'integer', defaultValue: 1, min: 1, max: 10 },
9898

9999
// Enumeration 枚举
100+
{ key: 'getAll', name: 'all', cliName: '--all', description: '枚举所有信息', category: 'enumeration', type: 'boolean' },
100101
{ key: 'getBanner', name: 'banner', cliName: '--banner', description: '获取DBMS Banner', category: 'enumeration', type: 'boolean' },
101102
{ key: 'getCurrentUser', name: 'current-user', cliName: '--current-user', description: '获取当前用户', category: 'enumeration', type: 'boolean' },
102103
{ key: 'getCurrentDb', name: 'current-db', cliName: '--current-db', description: '获取当前数据库', category: 'enumeration', type: 'boolean' },
104+
{ key: 'getHostname', name: 'hostname', cliName: '--hostname', description: '获取主机名', category: 'enumeration', type: 'boolean' },
103105
{ key: 'isDba', name: 'is-dba', cliName: '--is-dba', description: '检测是否为DBA', category: 'enumeration', type: 'boolean' },
104106
{ key: 'getUsers', name: 'users', cliName: '--users', description: '枚举用户', category: 'enumeration', type: 'boolean' },
107+
{ key: 'getPasswordHashes', name: 'passwords', cliName: '--passwords', description: '获取密码哈希', category: 'enumeration', type: 'boolean' },
108+
{ key: 'getPrivileges', name: 'privileges', cliName: '--privileges', description: '获取权限', category: 'enumeration', type: 'boolean' },
109+
{ key: 'getRoles', name: 'roles', cliName: '--roles', description: '获取角色', category: 'enumeration', type: 'boolean' },
105110
{ key: 'getDbs', name: 'dbs', cliName: '--dbs', description: '枚举数据库', category: 'enumeration', type: 'boolean' },
106111
{ key: 'getTables', name: 'tables', cliName: '--tables', description: '枚举表', category: 'enumeration', type: 'boolean' },
107112
{ key: 'getColumns', name: 'columns', cliName: '--columns', description: '枚举列', category: 'enumeration', type: 'boolean' },
113+
{ key: 'getSchema', name: 'schema', cliName: '--schema', description: '获取Schema', category: 'enumeration', type: 'boolean' },
114+
{ key: 'getCount', name: 'count', cliName: '--count', description: '获取记录数', category: 'enumeration', type: 'boolean' },
115+
{ key: 'getComments', name: 'comments', cliName: '--comments', description: '获取注释', category: 'enumeration', type: 'boolean' },
116+
{ key: 'getStatements', name: 'statements', cliName: '--statements', description: '获取SQL语句', category: 'enumeration', type: 'boolean' },
108117
{ key: 'dumpTable', name: 'dump', cliName: '--dump', description: '导出数据', category: 'enumeration', type: 'boolean' },
109118
{ key: 'dumpAll', name: 'dump-all', cliName: '--dump-all', description: '导出所有数据', category: 'enumeration', type: 'boolean' },
119+
{ key: 'dumpWhere', name: 'where', cliName: '--where', description: 'dump条件', category: 'enumeration', type: 'string' },
120+
{ key: 'limitStart', name: 'start', cliName: '--start', description: '起始行', category: 'enumeration', type: 'integer', min: 0 },
121+
{ key: 'limitStop', name: 'stop', cliName: '--stop', description: '结束行', category: 'enumeration', type: 'integer', min: 0 },
122+
{ key: 'firstChar', name: 'first', cliName: '--first', description: '起始字符位', category: 'enumeration', type: 'integer', min: 0 },
123+
{ key: 'lastChar', name: 'last', cliName: '--last', description: '结束字符位', category: 'enumeration', type: 'integer', min: 0 },
110124
{ key: 'db', name: 'database', cliName: '-D', description: '指定数据库', category: 'enumeration', type: 'string' },
111125
{ key: 'tbl', name: 'table', cliName: '-T', description: '指定表', category: 'enumeration', type: 'string' },
112126
{ key: 'col', name: 'column', cliName: '-C', description: '指定列', category: 'enumeration', type: 'string' },
113-
127+
{ key: 'extensiveFp', name: 'fingerprint', cliName: '--fingerprint', description: '深度指纹识别', category: 'enumeration', type: 'boolean' },
128+
129+
// Techniques 扩展
130+
{ key: 'uCols', name: 'union-cols', cliName: '--union-cols', description: 'UNION列数范围', category: 'techniques', type: 'string' },
131+
{ key: 'uChar', name: 'union-char', cliName: '--union-char', description: 'UNION填充字符', category: 'techniques', type: 'string' },
132+
{ key: 'uFrom', name: 'union-from', cliName: '--union-from', description: 'UNION FROM表', category: 'techniques', type: 'string' },
133+
{ key: 'uValues', name: 'union-values', cliName: '--union-values', description: 'UNION填充值', category: 'techniques', type: 'string' },
134+
114135
// General 通用
136+
{ key: 'requestFile', name: 'request file', cliName: '-r', description: 'HTTP请求文件', category: 'general', type: 'string' },
137+
{ key: 'sessionFile', name: 'session file', cliName: '-s', description: '会话文件', category: 'general', type: 'string' },
138+
{ key: 'trafficFile', name: 'traffic file', cliName: '-t', description: '流量日志文件', category: 'general', type: 'string' },
115139
{ key: 'batch', name: 'batch', cliName: '--batch', description: '批处理模式(不提问)', category: 'general', type: 'boolean', defaultValue: true },
116140
{ key: 'forms', name: 'forms', cliName: '--forms', description: '解析并测试表单', category: 'general', type: 'boolean' },
117141
{ key: 'crawlDepth', name: 'crawl', cliName: '--crawl', description: '爬取深度', category: 'general', type: 'integer', min: 0, max: 10 },
118142
{ key: 'flushSession', name: 'flush-session', cliName: '--flush-session', description: '刷新会话文件', category: 'general', type: 'boolean' },
119143
{ key: 'freshQueries', name: 'fresh-queries', cliName: '--fresh-queries', description: '忽略会话缓存', category: 'general', type: 'boolean' },
120-
{ key: 'verbose', name: 'verbose', cliName: '-v', description: '详细级别 (0-6)', category: 'general', type: 'integer', defaultValue: 1, min: 0, max: 6 }
144+
{ key: 'verbose', name: 'verbose', cliName: '-v', description: '详细级别 (0-6)', category: 'general', type: 'integer', defaultValue: 1, min: 0, max: 6 },
145+
{ key: 'answers', name: 'answers', cliName: '--answers', description: '预定义答案', category: 'general', type: 'string' },
146+
{ key: 'rParam', name: 'randomize', cliName: '--randomize', description: '随机化参数值', category: 'general', type: 'string' },
147+
{ key: 'evalCode', name: 'eval', cliName: '--eval', description: '执行Python代码', category: 'general', type: 'string' },
148+
{ key: 'shLib', name: 'shared-lib', cliName: '--shared-lib', description: '共享库路径', category: 'general', type: 'string' },
149+
{ key: 'regVal', name: 'reg-value', cliName: '--reg-value', description: '注册表值', category: 'general', type: 'string' },
121150
]
122151

123152
// 根据key获取参数定义
@@ -129,3 +158,58 @@ export function getParamDefinition(key: string): ParamDefinition | undefined {
129158
export function getParamByCliName(cliName: string): ParamDefinition | undefined {
130159
return PARAM_DEFINITIONS.find(p => p.cliName === cliName)
131160
}
161+
162+
// 驼峰转连字符(后备方案,用于映射表中未定义的参数)
163+
export function camelToKebab(str: string): string {
164+
return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '')
165+
}
166+
167+
// 创建 key→cliName 映射表(缓存用,避免每次遍历数组)
168+
let _paramKeyToCliNameMap: Map<string, string> | null = null
169+
export function getParamKeyToCliNameMap(): Map<string, string> {
170+
if (!_paramKeyToCliNameMap) {
171+
_paramKeyToCliNameMap = new Map<string, string>()
172+
for (const param of PARAM_DEFINITIONS) {
173+
_paramKeyToCliNameMap.set(param.key, param.cliName)
174+
}
175+
}
176+
return _paramKeyToCliNameMap
177+
}
178+
179+
// 转换单个参数为命令行格式
180+
export function convertOptionToCliArg(key: string, value: any): string | null {
181+
if (value === false || value === null || value === undefined || value === '') {
182+
return null
183+
}
184+
185+
const map = getParamKeyToCliNameMap()
186+
// 优先使用映射表中的 cliName
187+
let cliName = map.get(key)
188+
if (!cliName) {
189+
// 后备: 驼峰转连字符
190+
cliName = '--' + camelToKebab(key)
191+
}
192+
193+
if (value === true) {
194+
return cliName
195+
}
196+
197+
const formatted = formatCliValue(value)
198+
// 短选项(如 -v, -D 等)用空格分隔值
199+
if (/^-[a-zA-Z]$/.test(cliName)) {
200+
return `${cliName} ${formatted}`
201+
}
202+
return `${cliName}=${formatted}`
203+
}
204+
205+
// 格式化命令行参数值
206+
function formatCliValue(value: any): string {
207+
if (Array.isArray(value)) {
208+
return value.join(',')
209+
}
210+
const str = String(value)
211+
if (/[\s,;|&"']/.test(str)) {
212+
return `"${str.replace(/"/g, '\\"')}"`
213+
}
214+
return str
215+
}

src/frontEnd/src/views/TaskDetail/components/TaskOptions.vue

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import DataTable from 'primevue/datatable'
8484
import Column from 'primevue/column'
8585
import Tag from 'primevue/tag'
8686
import CommandLinePreview from '@/components/CommandLinePreview.vue'
87+
import { getParamDefinition, convertOptionToCliArg, camelToKebab } from '@/utils/paramDefinitions'
8788
8889
interface Props {
8990
task: Task | null
@@ -102,11 +103,14 @@ const viewOptions = [
102103
103104
const optionsList = computed(() => {
104105
if (!props.task?.options) return []
105-
return Object.entries(props.task.options).map(([name, value]) => ({
106-
name,
107-
value,
108-
description: getOptionDescription(name)
109-
}))
106+
return Object.entries(props.task.options).map(([name, value]) => {
107+
const paramDef = getParamDefinition(name)
108+
return {
109+
name: paramDef?.cliName || ('--' + camelToKebab(name)),
110+
value,
111+
description: paramDef?.description || getOptionDescription(name)
112+
}
113+
})
110114
})
111115
112116
const filteredOptions = computed(() => {
@@ -119,16 +123,17 @@ const filteredOptions = computed(() => {
119123
})
120124
121125
const commandLine = computed(() => {
122-
if (!props.task?.options) return 'sqlmap'
123-
124-
const args = Object.entries(props.task.options)
125-
.filter(([_, value]) => value !== false && value !== undefined && value !== null)
126-
.map(([key, value]) => {
127-
if (value === true) return `--${key}`
128-
return `--${key}=${value}`
129-
})
130-
131-
return `sqlmap ${args.join(' ')}`
126+
if (!props.task?.options) return 'python sqlmap.py'
127+
128+
const args: string[] = []
129+
for (const [key, value] of Object.entries(props.task.options)) {
130+
const arg = convertOptionToCliArg(key, value)
131+
if (arg) {
132+
args.push(arg)
133+
}
134+
}
135+
136+
return `python sqlmap.py ${args.join(' ')}`
132137
})
133138
134139
function getOptionDescription(name: string): string {

0 commit comments

Comments
 (0)