본문 바로가기

[학교] 시스템 보안 /Python

멀티 IO 웹 서버

import socket

import os

import time

import re

import urllib

import select



BACKLOG = 100

epoll = select.epoll()



class MainServer :

def __init__(self):

svrsock = 0;

os.chdir("/home/ohhyunkyu/httpd")

self.conn = 0;

def ServerRun (self):

svrsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

svrsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# socket bind

svrsock.bind(('192.168.201.120',80))

svrsock.listen(BACKLOG)

# epoll socket add 

epoll.register(svrsock.fileno(),select.EPOLLIN)

# Connection array

connections = {}  

self.responses = {}

while True :

# event listener

events = epoll.poll(1)

for fileno, event in events :

if fileno == svrsock.fileno() :

self.conn , (remhost,remport) = svrsock.accept()

self.conn.setblocking(0)

epoll.register(self.conn.fileno(),select.EPOLLIN)

connections[self.conn.fileno()] = self.conn

self.responses[self.conn.fileno()] = ''

elif event & select.EPOLLIN :

readText = connections[fileno].recv(1024)

if len(readText) > 1 :

self.ParsingText(fileno,readText)

elif event & select.EPOLLOUT :

connections[fileno].send(self.responses[fileno])

epoll.unregister(fileno)

connections[fileno].close()

def ParsingText(self,fileno,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 :

epoll.modify(fileno, select.EPOLLOUT)

self.responses[fileno] = "HTTP 403 Forbidden\r\nContent-Length: 13\r\n\r\n <h1>Forbidden"

return 0

if self.existsFileCheck(path) == -1 :

epoll.modify(fileno, select.EPOLLOUT)

self.responses[fileno] = "HTTP 404 OK\r\nContent-Length: 14\r\n\r\n<h1> Not Found"

return 0

if self.getParamCheck(fileno,path) == -1 :

epoll.modify(fileno, select.EPOLLOUT)

#self.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

epoll.modify(fileno, select.EPOLLOUT)

self.responses[fileno] = Content

else :

epoll.modify(fileno, select.EPOLLOUT)

self.responses[fileno] = self.Response(responseAddr)

  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(fileno, select.EPOLLOUT)

self.responses[fileno] = "HTTP 403 Forbidden\r\nContent-Length: 13\r\n\r\n <h1>Forbidden"

return 0

if self.existsFileCheck(path) == -1 :

epoll.modify(fileno, select.EPOLLOUT)

self.responses[fileno] = "HTTP 404 OK\r\nContent-Length: 14\r\n\r\n<h1> Not Found"

return 0

def PostMethodCheck(self,readText,DirPath,BodyContent) :

if self.QueryCheck(DirPath, BodyContent) :

return 0

else :

return -1

# common GET / POST Check Method

def UploadFolderCheck(self,path):

DirPath = str(path)

FolderAddr = DirPath[:DirPath.rfind("/")]

FileExt = DirPath[DirPath.rfind("."):]

spritFolders = FolderAddr.split("/")

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','&lt;')

BodyContent = BodyContent.replace('%3E','&gt;')

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('&lt.*?&gt',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 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();

def CallPageMake(self,path,Param) :

path = str(path)

f=open(path,'r+')

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):

files = open(File, 'r')

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

self.responses[fileno] = ERRORMESSAGE

epoll.modify(fileno, select.EPOLLOUT)

self.logWriter(DirPath, 100,Content)

class ServerStart :

acceptor = MainServer()

acceptor.ServerRun()

'[학교] 시스템 보안 > Python' 카테고리의 다른 글

Pipe 사용 예제  (0) 2014.04.10
쓰레드를 활용한 웹 서버  (0) 2013.12.26
멀티 IO / SubProc 활용한 웹 서버  (0) 2013.12.26
라이브러리 활용 예제  (0) 2013.12.26