python脚本代码审计webshell及挂马

闲来无事逛了下腾讯SRC,发现有个小工具,查杀webshell的,用perl写的,蛮早的了,2013年的了,于是用python也写了一个,方便
利用python脚本查webshell及挂马蛮方便的,这个脚本的功能,notepad++也可以实现,不过脚本比较灵活,方便扩展,比如扩展文件特征去识别也都可以,代码如下:

#!/usr/bin/python  
#coding:utf8
#author:ly55521

import os
import re
import sys
import time

reload(sys)
sys.setdefaultencoding('utf-8')

#定义代码目录
BASE_PATH = "D:\WWW"
#设定文件对象
file_object = os.walk(BASE_PATH)

#扩展名搜索过滤开关
extension_bool = 0 #默认0不开启,全部文件搜索,1开启
#要搜索包括的扩展名
extension = [".php",".asp",".jsp",]

#php webshell str
php_code_array = [
  r'\beval\(.*\)',
  r'\bassert\(.*\)',
  r'\bsystem\(.*\)',
  r'\bpassthru\(.*\)',
  r'\bexec\(.*\)',
  r'\bpcntl_exec\(.*\)',
  r'\bshell_exec\(.*\)',
  r'\bpopen\(.*\)',
  r'\bproc_open\(.*\)',
  r'\bpreg_replace\(.*\)',
  r'\bcreate_function\(.*\)',
  r'\bob_start\(.*\)',
  r'\barray_map\(.*\)',
  #r'(include|include_once|require|require_once)',
  #r'(phpspy|4ngel|wofeiwo|c99shell|webshell|php_nst|reDuh)',
]
asp_code_array = [
  '',
]
#asp.net webshell str
aspx_code_array = [
  '',
]
#jsp webshell str
jsp_code_array = [
  '',
]


def search_webshell(file_path):
    with open(file_path,'r') as f: #"D:\\phpStudy\\WWW\\1.php"
        #每次读取文件一行
        line = f.readline()
        #记录行数
        i = 1
        #循环遍历文件
        while line:
            #打印行数和行数内容
            #print i,"==>",line,
            #读取行文件
            line = f.readline()  
            #增加空行处理
            #print line
            #if line:
            #  print "yes"
            #else:
            #  print "no"
            #time.sleep(2)
            #
            #
            #记录行数
            i = i+1
            if line:
              #判断是否存在
              for re_str in php_code_array:
                  #print type(re_str)
                  code_Pattern = re.compile(re_str,re.I)
                  match = code_Pattern.search(line)
                  if match:
                      print u"文件路径:",file_path,u"文件行数:",i,"--->",u"匹配关键字:",re_str
                      print u"文件内容:",line
                      #写入文件日志记录
                      with open('access.log',"a") as log:
                        log.write("文件路径:"+file_path+"文件行数:"+str(i)+"--->"+"匹配关键字:"+re_str+
                          "文件内容:"+line+"\n")

if __name__ == '__main__':
    #遍历目录
    for path,dirlist,filelist in file_object:
        for filename in filelist:
            #print os.path.join(path,filename)
            #处理扩展名
            if extension_bool:
              #开启扩展名过滤时,判断文件扩展名是否在列表里
              if os.path.splitext(filename)[-1] in extension:
                #print os.path.join(path,filename)
                search_webshell(os.path.join(path,filename))
            else:
              #默认未开启扩展名过滤,全部文件搜索
              search_webshell(os.path.join(path,filename))

腾讯SRC perl原版链接:https://security.tencent.com/index.php/opensource/detail/2
CF_HB推荐个更完美的东东:https://github.com/xiaoqin00/00scanner/tree/master/00Scanner/findShell/FindShell

python CGI程序部署在phpstudy共享Apache环境

找到 http.conf 添加下面配置:(如果存在则修改或者去掉注视修改即可)
默认配置目录:C:\phpStudy\Apache\conf\httpd.conf

# add cgi python
<Directory "C:/Users/administrator/Desktop/360_cgi-bin"> #设置目录的访问权限
    AllowOverride None
    Options Indexes FollowSymLinks ExecCGI
    Require all granted
</Directory>

<IfModule alias_module> #设置虚拟目录
ScriptAlias /cgi-bin/ "C:/Users/administrator/Desktop/360_cgi-bin"
</IfModule>

<IfModule mime_module> #定义处理的扩展名
AddHandler cgi-script .cgi .py
</IfModule>

<IfModule dir_module> #配置目录首索引文件
    DirectoryIndex index.html index.php index.htm l.php index.py
</IfModule>

上面仅是对独立主机,如果共享主机还要配置 vhosts.conf
(Include conf/vhosts.conf #这里引用了这个虚拟主机配置文件)
增加如下配置:

<VirtualHost *:80>
    DocumentRoot "C:\Users\administrator\Desktop\360_cgi-bin"
    ServerName 127.0.0.4
    ServerAlias
  <Directory "C:\Users\administrator\Desktop\360_cgi-bin">
      Options FollowSymLinks ExecCGI
      AllowOverride All
      Order allow,deny
      Allow from all
      Require all granted
  </Directory>
</VirtualHost>

其中127.0.0.4为要绑定的域名,修改完后需要重启apache,linux配置一样,估计问题会多一些;
http://127.0.0.4/index.py 必须设置py路径,编码和响应类型及空行

#!C:\python27\python.exe
# -*- coding: utf-8 -*-
print "Content-type:text/html"
print                               # 空行,告诉服务器结束头部
#!C:\python27\python.exe
# -*- coding: utf-8 -*-
print "Content-type:text/html"
print                               # 空行,告诉服务器结束头部
print '<html>'
print '<head>'
print '<meta charset="utf-8">'
print '<title>Hello Word - 我的第一个 CGI 程序!</title>'
print '</head>'
print '<body>'
print '<h2>Hello Word! 我是CGI程序</h2>'
print '</body>'
print '</html>'

队列+多线程扫描内网80,8080存活主机脚本

#!-*- coding:utf-8 -*-
import sys,requests,re,os
import threading,Queue
"""
#单线程运行
ip = "10.1.5.0"
count = 5
file_object = open('host.txt', 'w')

ip2 = int(ip.split('.')[-2])
ip1 = int(ip.split('.')[-1])
ip_before = '%s.%s' % (ip.split('.')[0], ip.split('.')[1])
for i in xrange(0,count,1):
    new_ip1 = ip1 + i
    if  11 <= new_ip1 <= 254:
        print '%s.%s.%s' % (ip_before, str(ip2), str(new_ip1))
    else:
        new_ip2 = ip2 + int(new_ip1/254)
        new_ip1 = new_ip1%254 + 10
        host = '%s.%s.%s' % (ip_before, str(new_ip2), str(new_ip1))
        try:
            print host,
            url1="http://"+host
            url2="http://"+host+":8080"
            if requests.get(url=url1,timeout=5).status_code == 200 :
                file_object.write(url1+"\n")
                print url1,u"success"
            elif requests.get(url=url2,timeout=5).status_code == 200 :
                file_object.write(url2+"\n")
                print url2,u"success"
        except:
            print host,u"error"
else:
    file_object.close()
    print u"end!!!"
"""

#转换 ip函数
def ip2num(ip):
    ip=[int(x) for x in ip.split('.')]
    return ip[0] <<24 | ip[1]<<16 | ip[2]<<8 |ip[3]
def num2ip(num):
    return '%s.%s.%s.%s' %( (num & 0xff000000) >>24,
                            (num & 0x00ff0000) >>16,
                            (num & 0x0000ff00) >>8,
                            num & 0x000000ff )
def get_ip(ip):
    start,end = [ip2num(x) for x in ip.split('-') ]
    return [ num2ip(num) for num in xrange(start,end+1) if num & 0xff ]

#线程类
class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID #线程id,每次增加1
        self.name = name #线程名从1到NUM
        self.q = q #队列标识
    def run(self):
        process_data(self.name, self.q)

# 调用线程 执行
def process_data(threadName, q):
    #循环条件:exitFlag=0
    while not exitFlag:
        #线程加锁
        queueLock.acquire()
        #如果队列不为空
        if not workQueue.empty():
            #不为空则获取队列
            ip = q.get()
            queueLock.release()
            #线程解锁
            print  u"\n\n[loading: %s/%s ]" % (zongshu - workQueue.qsize(), zongshu)
            #自定义需要验证/实现的方法
            _verify(ip)
        else:
            #线程解锁
            queueLock.release()

def _verify(host):
    #实现可以打开的话,则保存到文件中
    with open('host.txt', 'a') as file_object:
        try:
            #print host,
            url1="http://"+host
            url2="http://"+host+":8080"
            response1=requests.get(url=url1,timeout=5)
            response2=requests.get(url=url2,timeout=5)

            if response1.status_code == 200 :
                title = re.findall(r'<title>(.*?)</title>', response1.content)[0]
                file_object.write(url1+','+title+"\n")
                print url1,title,u"success"
            if response2.status_code == 200 :
                title = re.findall(r'<title>(.*?)</title>', response2.content)[0]
                file_object.write(url2+','+title+"\n")
                print url2,title,u"success"
        except:
            print host,u"error"
        #file_object.close()


if __name__ == '__main__':
    # 线程退出标志
    exitFlag = 0
    #默认线程数量
    NUM = 100
    #线程锁
    queueLock = threading.Lock()
    #消息队列
    workQueue = Queue.Queue()
    #线程列表
    threads = []
    #队列线程id
    threadID = 1

    #创建线程组(一个列表)
    for tName in xrange(1,NUM):
        #调用线程类
        thread = myThread(threadID, tName, workQueue)
        #线程开始
        thread.start()
        #添加到线程列表
        threads.append(thread)
        #线程id自增
        threadID += 1        

    #获取ip列表
    #iplist=get_ip('10.132.0.0-10.132.255.255')
    iplist=get_ip('10.1.0.0-10.1.255.255')

    #获取ip列表总数
    zongshu=len(iplist)

    #加锁
    queueLock.acquire()
    #遍历ip列表
    for host in iplist:
        #添加到队列
        workQueue.put(host)
    #解锁
    queueLock.release()

    #等待队列清空
    while not workQueue.empty():
        pass
   
    # 通知线程退出
    exitFlag = 1

    # 等待所有线程完成`
    for t in threads:
        #线程阻塞
        t.join()

    print u"-- END  ---"

python yield生成器的理解

python yield生成器,在爬虫框架Scrapy时,都是用的yield,而在实际使用中确没有使用过,网上说的都是生成斐波那契數列,可能是抄袭,反正对于数学不好的人看了是不好理解的,yield的应用场景是在一个函数需要返回一个很大的可迭代对象,如果一次返回的话会占用很大的内存,做爬虫如果数据量较少感觉不到,如果一个函数需要返回亿万级数据,一次返回结果后,还要去迭代这个列表,占用内存比较大,用yidld就可用解决这个问题,貌似实现连接池用yield噢,简单写了个实例代码,如下:

#coding=utf-8
import sys

def test():
    for i in xrange(5):
        yield i

s1 = test()
s2 = test()

print type(s1),s1,type(s2),s2

try:
    print "依次调用--"
    print s1.next(),s1.next(),s1.next(),s1.next(),s1.next(),s1.next(),#next迭代器内置对象
except Exception, e:
    print "\n迭代调用--"
    for i in s2:
        print i

Debian Python 多版本共存 pyenv

#安装python多版本共存管理 pyenv
apt-get install curl
curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

#工作环境中添加
vi ~/.bashrc

# Load pyenv automatically by adding
# the following to ~/.bash_profile:

export PATH=”/root/.pyenv/bin:$PATH”
eval “$(pyenv init -)”
eval “$(pyenv virtualenv-init -)”

#退出重新打开控制台
pyenv install –list #查看可安装的版本

pyenv install 3.1 #安装3.1
pyenv install 2.7.11 #安装2.7.11

pyenv global 3.1 #切换为3.1版本为当前环境

pyenv version #查看当前版本
pyenv versions #查看已安装的python版本

使用 spynner 欺骗流量统计

首先流量统计的标准 一般分为两种情况,第一种是后端直接做的统计,这种比较好搞,直接requests获取源代码就可以统计到了,但是大部分是另一种情况,使用CNZZ站长统计、百度统计等,这类统计使用外部js加载统计的,这种直接使用requests获取源代码理论是解析不了js的,没去测试,想了一种模拟真实浏览器去解析js请求,使用spynner扩展就可以了,然后需要获取有效代理,使用有效代理访问就可以了。

看了下主要用到 spynner 的下面几个方法:

#创建一个浏览器对象
browser = spynner.Browser()

#打开浏览器,并隐藏或显示
browser.hide()
browser.show()

#webkit加载页面
browser.load(“http://www.0535code.com”)
print browser.html.encode(“utf-8″)

#关闭该浏览器并删掉实例
browser.close()
del browser

#使用jquery
browser.load_jquery(True)
browser.radio(‘#radiomea’)
browser.wk_check(‘#checkmea’)
browser.check(‘#checkmea’)
browser.wk_select(‘#sel’, ‘aa’)
browser.select(‘#asel option[name=”bbb”]’, remove=False)
browser.fill(‘input[name=”w”]’, ‘foo’)

#配置代理和使用代理下载
browser.set_proxy(‘http://127.0.0.1:8888’)
browser.download(‘http://0535code.com’, proxy_url=’http://127.0.0.1:8888′)

更多使用参考:
https://pypi.python.org/pypi/spynner

Django数据库模型字段类型汇总

Django数据库模型的字段类型汇总

models.CharField(max_length=250) #varchar,普通可变字符串
models.EmailField(max_length=100) #varchar,邮箱可变字符串
models.URLField(verify_exists=True, max_length=200,) #varchar,URL可变字符串
models.IPAddressField(max_length=100) #varchar, IP
models.FilePathField(path=None, match=None, recursive=False, max_length=100,) #varchar,文件路径
models.SlugField(max_length=50,) #varchar,索引标签

models.IntegerField() #int
models.PositiveIntegerField() #int 正整数
models.SmallIntegerField() #smallint
models.PositiveSmallIntegerField([**options]) #smallint 正整数
models.AutoField() #int;一个自动递增的整型字段,添加记录时它会自动增长。你通常不需要直接使用这个字段;如果你不指定主键的话,系统会自动添加一个主键字段到你的model
models.DecimalField(max_digits=None, decimal_places=None) #decimal
models.FloatField() #real

models.BooleanField() #boolean或bit,布尔类型
models.NullBooleanField() #bit字段上可以设置上null值

models.DateField(auto_now=False, auto_now_add=False,) #date
#auto_now最后修改记录的日期;auto_now_add添加记录的日期
models.DateTimeField(auto_now=False,auto_now_add=False,) #datetime
models.TimeField(auto_now=False, auto_now_add=False,) #time

models.TextField() #text
models.XMLField(schema_path=None)#text

models.ForeignKey(othermodel) #外键,关联其它模型,创建关联索引
models.ManyToManyField(othermodel) #多对多,关联其它模型,创建关联表
models.OneToOneField(othermodel, parent_link=False,)#一对一,字段关联表属性

#详细介绍参考:http://www.cnblogs.com/lhj588/archive/2012/05/24/2516040.html

python实现端口复用

当一个端口想在不同情况下转发到本地不同端口下,python实现端口复用就有用了,代码如下:

参考:http://www.jb51.net/article/51809.htm
http://blog.csdn.net/liujiayu2/article/details/40450823

#coding=utf-8
import socket
import sys
import select #异步
import threading
##http://www.jb51.net/article/51809.htm
##http://blog.csdn.net/liujiayu2/article/details/40450823

class Thread(threading.Thread):
  def __init__(self,buf,sockfd):
    threading.Thread.__init__(self)
    self.buf=buf
    self.sockfd=sockfd
  def run(self):
   if len(self.buf)!=0:
    if 'GET' in self.buf :  #判断是否是浏览器提交的数据如果是则将提交的数据转发至本地环回地址的80端口
     s2=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     s2.connect(('127.0.0.1',80))
     s2.send(self.buf)
     bufer=''
     while 1:
      recv_data=s2.recv(1024)
      bufer+=recv_data
      if len(recv_data)==0:
       break
     print bufer,len(bufer)
     if len(bufer)==0:
      pass    
     self.sockfd.send(bufer)  #将服务器发送的数据发回客户端
     s2.close
     self.sockfd.close
     sys.exit()
    else:
     'ps:connect to ssh' #如果数据不是浏览器提交则将其转发至本地的22端口
     s2=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     s2.connect(('127.0.0.1',22))
     s2.send(self.buf)
     recv_data=s2.recv(4096)
     conn.send(recv_data)
     self.sockfd.send(bufer)  #将服务器发送的数据发回客户端    
     self.sockfd.close
     s2.close

host='0.0.0.0'
port=5210    
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) #端口复用的关键点
s.bind((host,port))
s.listen(10)

while 1:
 infds,outfds,errfds=select.select([s,],[],[],5) #使用select函数进行非阻塞操作
 if len(infds)!=0:
  conn,(addr,port)=s.accept()
  print 'connected by',addr,port
  data=conn.recv(1024)
  t=Thread(data,conn)
  t.start()
s.close

python做端口服务banner识别

之前看过别人做mongo的POC,用socket实现的,这里也可能不应该叫做POC,叫做端口扫描,mongo默认端口是27017,细心的人可能发现mongo不仅支持TCP连接,而且还支持HTTP连接,HTTP会给出一个成功的响应,例如:mongo
You are trying to access MongoDB on the native driver port. For http diagnostic access, add 1000 to the port number
就可以针对 mongo 未授权和已授权,不同环境下的响应作出POC,虽然这不是最好的,也要给我们WEB狗一条生路,不能赶进杀绝吧。
而27017这个端口也实现了TCP连接,一个端口多种用途,这就是所谓的端口复用了,可能是考虑到用户体验? 某些应用总是会使用端口复用,端口复用如果用在rootkit里面+iptables规则会很隐蔽的哦,能做好事亦能做坏事;

回到做端口对应服务banner识别,一直用nmap识别端口,nmap是怎么实现的端口服务种类呢,有的是http有的是tcp或udp,http的比较好说,通过response判断就可以了,甚至肉眼直接看就好了,tcp和udp的可以用telnet去连接看返回的banner,tcp的比http的复杂,又分为主动式banner和被动式banner,主动式banner就是连接后会返回banner,被动式banner就是连接后,需要发消息告诉他我要banner信息,他会返回banner信息;

朋友给了一个案例:https://github.com/AnthraX1/InsightScan/blob/master/scanner.py 工具写的比较全噢、

原本是研究的 jdwp,其中发现一些问题,通过http和tcp两种情况来判断 jdwp服务是否存在、

1.socket 可以获取banner, http 是关闭的(可以获取banner);
2.scoket不可以获取banner,http是开启的(可以获取banner);
3.socket 可以获取banner, http 是开启的(都可以获取banner) 忽略这种情况;
4.scoket不可以获取banner,http是关闭的(都不可以获取banner)忽略这种情况;

针对 2.scoket不可以获取banner,http是开启的(可以获取banner) 这种情况 如果不存在的话, POC 只有1个, 如果存在的话 POC 会有2个。 这种情况一般不会把http转换为tcp去用,如果确定socket开启的话也是有可能存在漏洞的!jdwp 服务正常应该也类似mongo使用的端口复用吧,实际中利用 jdwp 是使用tcp协议,所以不用去管http是否开启了,POC如下:

# -*- coding: utf-8 -*-
import sys,socket
reload(sys)
sys.setdefaultencoding( "utf-8" )
#设置socket超时时间
socket.setdefaulttimeout(0.5)

ip = "10.1.5.243"
port = 8000

connet_res = socket.socket()
response = connet_res.connect_ex((ip, port))
if response == 0:
  connet_res.send('testing')#被动连接时
  banner = connet_res.recv(1024)#阻塞函数
  connet_res.close()
print u'当前端口:',port,u'Banner信息为:',banner

通过nmap识别端口服务:
nmap -p 8000 –script=banner 127.0.0.1 #普通方式识别 -》 HTTP
nmap -p 8000 –script=banner 127.0.0.1 -Pn #深入识别 -》 TCP

python实现端口转发

之前一直用花生壳做外网端口转发,但是花生壳是不稳定的噢,一次同事虚拟机用的NAT模式,虚拟机的IP地址相当于内网的内网了,内网C段是无法直接访问的,比如#有A、B、C三台计算机,A、B互通,B、C互通,但是A、C不通,通过端口转发解决A与C的互通问题,网上找到有人写的代码,加了一些注释直接用了,或许在某些场景下可以有更复杂的应用也不好说,代码如下:

本文代码来自:#http://www.tuicool.com/articles/VRNzaq
socket相关学习:http://yangrong.blog.51cto.com/6945369/1339593/

也可以通过这个端口转发做监控噢、

# -*- coding: utf-8 -*-
import socket
import threading

# 端口映射配置信息
CFG_REMOTE_IP = '192.168.23.133' #要转发的IP
CFG_REMOTE_PORT = 80 #要转发的端口
CFG_LOCAL_IP = '0.0.0.0' #连接IP白名单
CFG_LOCAL_PORT = 5210 #连接的端口号

# 接收数据缓存大小
PKT_BUFF_SIZE = 2048

#调试日志封装
def send_log(content):
    print content
    return

#单向流数据传递
def tcp_mapping_worker(conn_receiver, conn_sender):
    #开始消息循环
    while True:
        try:
            #接受数据
            data = conn_receiver.recv(PKT_BUFF_SIZE)
        except Exception:
            send_log('Event: Connection closed.')
            break
        #没有数据时,打印信息
        if not data:
            send_log('Info: No more data is received.')
            break
        #传递消息
        try:
            conn_sender.sendall(data)
        except Exception:
            send_log('Error: Failed sending data.')
            break

        # send_log('Info: Mapping data > %s ' % repr(data))
        send_log('Info: Mapping > %s -> %s > %d bytes.' % (conn_receiver.getpeername(), conn_sender.getpeername(), len(data)))

    conn_receiver.close()
    conn_sender.close()

    return

#端口映射请求处理
def tcp_mapping_request(local_conn, remote_ip, remote_port):
    #建立TCP流
    remote_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        #连接要转发的ip和端口
        remote_conn.connect((remote_ip, remote_port))
    except Exception:
        local_conn.close()
        send_log('Error: Unable to connect to the remote server.')
        return
    #把args参数传给tcp_mapping_worker函数做并发处理,开始线程
    threading.Thread(target=tcp_mapping_worker, args=(local_conn, remote_conn)).start()
    threading.Thread(target=tcp_mapping_worker, args=(remote_conn, local_conn)).start()

    return

#端口映射函数
def tcp_mapping(remote_ip, remote_port, local_ip, local_port):
    #建立TCP流
    local_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #绑定IP和端口
    local_server.bind((local_ip, local_port))
    #指定挂起数,开始监听端口
    local_server.listen(5)
    #打印日志
    send_log('Event: Starting mapping service on ' + local_ip + ':' + str(local_port) + ' ...')
    #开始消息循环
    while True:
        try:
            #接受消息并返回
            (local_conn, local_addr) = local_server.accept()
        except KeyboardInterrupt, Exception:
            local_server.close()
            send_log('Event: Stop mapping service.')
            break
        #把args参数传给tcp_mapping_request函数做并发处理,开始线程
        threading.Thread(target=tcp_mapping_request, args=(local_conn, remote_ip, remote_port)).start()
        #打印日志
        send_log('Event: Receive mapping request from %s:%d.' % local_addr)
    return

# 主函数
if __name__ == '__main__':
    tcp_mapping(CFG_REMOTE_IP, CFG_REMOTE_PORT, CFG_LOCAL_IP, CFG_LOCAL_PORT)