'''
Created on 2013. 12. 10.
@author: root
'''
import socket
from threading import Thread
import os
import time
import re
import urllib
import select
from multiprocessing import Process,Queue
BACKLOG = 100
epoll = select.epoll()
queue = Queue()
# socket Programing Ref
#http://jinpyolab.tistory.com/1
# http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Python/Socket
connections = {}
responses = {}
##
## Multi IO & subProc Data Job
##
#
class NewServer :
def __init__(self) :
os.chdir("/home/ohhyunkyu/httpd")
def serverRun(self) :
try :
svrsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#socket Option
svrsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
svrsock.bind(('192.168.201.120',80))
# MAC CONNECT CNT
svrsock.listen(BACKLOG)
except :
print "Bind Error"
else :
#Pattern = []
#for i in xrange(0, 10):
# Pattern.append(ContentPattern())
epoll.register(svrsock.fileno(),select.EPOLLIN)
while True :
events = epoll.poll(1)
for fileno, event in events :
if fileno == svrsock.fileno() :
try :
conn , (remhost,remport) = svrsock.accept()
conn.setblocking(0)
connections[conn.fileno()] = conn
epoll.register(conn.fileno(),select.EPOLLIN)
except Exception,e :
print "Accept Error" + e
elif event & select.EPOLLIN :
try :
readText = connections[fileno].recv(1024)
if len(readText) > 1 :
content = ContentPattern(fileno,readText,queue)
content.start()
except :
print "recv Error"
elif event & select.EPOLLOUT :
#print "POLLOUT" + str(fileno)
#print str(responses[fileno])
#print responses
try :
connections[fileno].send(queue.get())
epoll.unregister(fileno)
connections[fileno].close()
except :
print "send Error"
class ContentPattern(Process) :
readText = ''
def __init__ (self,fileno,readText, queue):
Process.__init__(self) # Thread Init
self.readText = readText
self.fileno = fileno
def run(self): # Thread Runnable
self.ParsingText(self.readText)
def ParsingText(self,readText) :
contentLine = str(readText).splitlines()
type = str(contentLine[0]).split(" ")[0]
path = str(contentLine[0]).split(" ")[1]
version = str(contentLine[0]).split(" ")[2]
if str(type).upper() == "GET" :
if path == "/" :
path += "main.html"
# Rule inject
if self.UploadFolderCheck(path) == -1 :
queue.put("HTTP 403 Forbidden\r\nContent-Length: 13\r\n\r\n <h1>Forbidden")
epoll.modify(self.fileno,select.EPOLLOUT)
return 0
if self.existsFileCheck(path) == -1 :
queue.put("HTTP 404 OK\r\nContent-Length: 14\r\n\r\n<h1> Not Found")
epoll.modify(self.fileno, select.EPOLLOUT)
return 0
if self.getParamCheck(self.fileno,path) == -1 :
epoll.modify(self.fileno, select.EPOLLOUT)
#responses[fileno] = "HTTP 400 Bad Request\r\nContent-Length: 15\r\n\r\n<h1>Bad Request"
return 0
responseAddr = str(path)[1:]
if responseAddr.find("?") > 0 :
fName = responseAddr[:responseAddr.find("?")] # request File Name Parsing
Param = responseAddr[responseAddr.find("?")+1:] # request Parameter Parsing
Content = self.CallPageMake(fName, Param) # response Page Make
queue.put(Content)
epoll.modify(self.fileno, select.EPOLLOUT)
return 0
else : #TODO
queue.put(self.Response(responseAddr))
epoll.modify(self.fileno, select.EPOLLOUT)
return 0
elif str(type).upper() == "POST" :
BodyContent = str(readText)[str(readText).find("\r\n\r\n"):] # BydyContent Parsing
BodyContent = self.replaceBodyContent(BodyContent) # Decoding
if self.UploadFolderCheck(path) == -1 :
epoll.modify(self.fileno, select.EPOLLOUT)
queue.put("HTTP 403 Forbidden\r\nContent-Length: 13\r\n\r\n <h1>Forbidden")
return 0
if self.existsFileCheck(path) == -1 :
epoll.modify(self.fileno, select.EPOLLOUT)
queue.put("HTTP 404 OK\r\nContent-Length: 14\r\n\r\n<h1> Not Found")
return 0
if self.PostMethodCheck(self.fileno,path, BodyContent) == -1 :
return 0
content = self.CallPageMake(path[1:], BodyContent)
queue.put(content)
epoll.modify(self.fileno, select.EPOLLOUT)
def PostMethodCheck(self,fileno,DirPath,BodyContent) :
if self.QueryCheck(fileno,DirPath, BodyContent) :
return 0
else :
return -1
# common GET / POST Check Method
def UploadFolderCheck(self,path):
try :
DirPath = str(path)
FolderAddr = DirPath[:DirPath.rfind("/")]
FileExt = DirPath[DirPath.rfind("."):]
spritFolders = FolderAddr.split("/")
except :
print "Upload Exception"
else :
i=0
while i < len(spritFolders) :
if len(spritFolders)-1 == i : # Last Folder = Upload
if str(FileExt) == ".asp" or str(FileExt) == ".exe" : # Execute File Ext .asp or .exe
return -1
i = i +1
return 0
def existsFileCheck(self,DirPath):
if str(DirPath).find("?") > 0 : # Param Ext
Path = str(DirPath).split("?")[0][1:]
if os.path.exists(str(Path)) == True :
return 0
else :
return -1
else :
if os.path.exists(str(DirPath)[1:]) == True :
return 0
else :
return -1
# GET Request Parameter Check
def getParamCheck(self,fileno,path):
path = self.replaceBodyContent(path)
if str(path).find("&") > 0 :
BodyContent = path[path.find("?")+1:]
BodyContent = self.replaceBodyContent(BodyContent)
queryList = str(BodyContent).split("&")
i = 0
while i < len(queryList) : # Query Check
if self.QueryCheck(fileno,path, queryList[i]) :
i=i+1
else :
return -1
return 0
else : # if Param Count = 1
tmp = str(path)
BodyContent = tmp[tmp.find("?")+1:]
BodyContent = self.replaceBodyContent(BodyContent)
if self.QueryCheck(fileno,path, BodyContent) :
return 0
else :
return -1
def replaceBodyContent(self,BodyContent):
BodyContent = BodyContent.replace('%3C','<')
BodyContent = BodyContent.replace('%3E','>')
BodyContent = BodyContent.replace("%2F", "/")
BodyContent = BodyContent.replace("%7C","|")
BodyContent = BodyContent.replace("%27", "'")
BodyContent = BodyContent.replace("%28", "(")
BodyContent = BodyContent.replace("%29", ")")
BodyContent = BodyContent.replace("+", " ")
BodyContent = BodyContent.replace("%2B", "+")
BodyContent = BodyContent.replace("%E2","\'")
BodyContent = BodyContent.replace("%22","\"")
BodyContent = BodyContent.replace("%25","%")
BodyContent = BodyContent.replace("%3D","=")
BodyContent = BodyContent.replace("%5B","[")
BodyContent = BodyContent.replace("%5D","]")
BodyContent = BodyContent.replace("%3F","?")
BodyContent = BodyContent.replace("%40", "@")
BodyContent = BodyContent.replace("%26", "&")
return BodyContent
def QueryCheck(self,fileno,DirPath,BodyContent) :
# ref page : http://codeflow.co.kr/question/1061/
pattern = re.search('<.*?>',BodyContent)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
pattern = re.search('location[\=\s\w\%d]*http',BodyContent,re.IGNORECASE)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath, Content)
return False
# pattern is %00 ' and sql injection
pattern = re.search('%00?[\'\s]+\w*',BodyContent,re.IGNORECASE)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is select @@version
pattern = re.search('select[\s]*[%\d]*@@version',BodyContent,re.IGNORECASE)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is Group by having injection
pattern = re.search('group[\s]*[%\d]*by[\s]*[%\d]*[\w]*[\s]having',BodyContent,re.IGNORECASE)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is parameter parsing and 'or find it.
pattern = re.search('[\'\s\)\(]*or[\'\s\"\)\(]*\w*=[\'\s\"]*\w*[\'\s\"]*',BodyContent,re.IGNORECASE)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is parameter parsing and find it.
pattern = re.search('[\'\s]*and[\[\s]*[\w\s]*[\]\s]?[\w\s]*and',BodyContent,re.IGNORECASE)
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is 'sel'||'ect' or 'sel'+'ect' || 'sel' 'ect'
pattern = re.search('\w*[\'\"][\|\"\'\+\s]*[\'\"]\w*',BodyContent,re.IGNORECASE) # "SEL\" \"ECT" ..
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
#Cscript%3Etest%3C%2Fscript%3E # pattern is char(85) or chr(85) ...
pattern = re.search('char[\( \s]*[0-9]*[\)\s]*',BodyContent,re.IGNORECASE) # char(85) test ..
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is select * from table where < - full operation find it.
pattern = re.search('select[%\d\s]*\w*[\s]*from',BodyContent,re.IGNORECASE) # select * from table where
# where line - > [%\d\s]*\w*[\s]*where
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is union ~~ from < - full operation find it
pattern = re.search('union[%\d\s]*\w*[%\d\s]*\w*[%\d\s]*from',BodyContent,re.IGNORECASE) # union select * from
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
# pattern is insert into( ) values < -- full operation find it
pattern = re.search('insert[%\d\s]into[%\d\s\w \( \)]* values',BodyContent,re.IGNORECASE) # insert into values
if pattern :
Content = pattern.group()
self.ErroSqlexecute(fileno,DirPath,Content)
return False
return True
def CallPageMake(self,path,Param) :
try:
path = str(path)
f=open(path,'r+')
except IOError :
print "Call Page Make FileOpen Error"
else :
tmp = f.readlines()
writetmp=""
for line in tmp:
if str(line).find("<t1") > 1 :
writetmp+=line
writetmp+="<br>"+ Param
else :
writetmp+=line
f.seek(0,0)
f.write(writetmp)
f.close();
if path.find(".ohk") > 0 :
os.system("cp mresult.ohk result.ohk")
else :
os.system("cp mresult.html result.html")
return writetmp
def Response(self,File):
try :
files = open(File, 'r')
except IOError :
print "Response FileOpen Error"
else :
lines = files.readlines()
FileLen = os.path.getsize(str(File))
nowTmp = time.asctime()
tmp = nowTmp.split()
timeString = tmp[0] + ", " + tmp[2] +" "+tmp[1]+ " "+tmp[4] + " "+tmp[3] + " GMT"
SendFileHeader = "HTTP/1.1 200 OK \r\nDate: "+timeString+"\r\nExpires:" +timeString +"\r\nCache-Control: no-store,no-cache,must-revalidate\r\nContent-Length: "+str(FileLen)+"\r\nConnection: close\r\nContent-Type : text/html\r\n\r\n"
#self.conn.sendall(SendFileHeader)
for line in lines:
#encoding = self.replaceBodyContent(str(line))
encoding = str(line)
SendFileHeader+=encoding
files.close()
return SendFileHeader
def ErroSqlexecute(self,fileno,DirPath,Content):
lens = len(Content)+24
ERRORMESSAGE = "HTTP 400 Bad Request\r\nContent-Length:"+str(lens)+" \r\n\r\n <h1>SQL Syntax Error : "+Content
queue.put(ERRORMESSAGE)
epoll.modify(fileno, select.EPOLLOUT)
self.logWriter(DirPath, 100,Content)
def logWriter(self,path,type,content):
# f = open("WebServerLog.log",'a')
# peerAddress = str(self.conn.getpeername())
# nowTmp = time.localtime()
# now = "%02d-%02d-%02d %02d:%02d:%02d" % (nowTmp.tm_year, nowTmp.tm_mon, nowTmp.tm_mday , nowTmp.tm_hour , nowTmp.tm_min, nowTmp.tm_sec)
# Path = str(path)
#
# LogContent ="Time=" + now + " Path=" + Path + " Connect=" + peerAddress
#
# if type == 404 : # File Not Found
# LogContent += " Event=File Not Found\r\n"
# elif type == 400 : # Big Param , ' - Param
# LogContent += " Event=Bad Request \r\n"
# elif type == 403 : # WebShell
# LogContent += " Event=Warning Extention \r\n"
# elif type == 50 : # Post Content Script
# LogContent += " Event=Body Content Script \r\n"
# elif type == 100 :
# LogContent += " Event= Sql Injection : "+ content + "\r\n"
# else :
# LogContent += " Event=NULL Event \r\n"
#
# f.write(LogContent)
# f.close();
return 0
class ServerStart :
newServer = NewServer()
newServer.serverRun()
'[학교] 시스템 보안 > Python' 카테고리의 다른 글
Pipe 사용 예제 (0) | 2014.04.10 |
---|---|
멀티 IO 웹 서버 (0) | 2013.12.26 |
쓰레드를 활용한 웹 서버 (0) | 2013.12.26 |
라이브러리 활용 예제 (0) | 2013.12.26 |