網上代碼只見了http://sourceforge.net/projects/sqlxsswaf/?source=directory
開始read!
一: 主函數
流程很清晰,
1. 整個WAF主函數體為死迴圈,在while(1)的程式碼片段中,當代碼處理完畢當前日誌內容後,睡眠10ms,繼續從get_pos處向後處理新內容。
2. 第二個while處理日誌,當找到get或post為開頭的日誌內容後,對用戶端發送來的命令進行檢測,直到文檔結尾。然後到達1中while迴圈的末尾
#define tailer "/var/log/apache2/access.log"
#define finder "GET"
#define finder2 "POST"
int main(void){ fpos_t get_pos; printf("SQLXSSGRABBER HAS STARTED\n"); while(1) {FILE *fp = fopen(tailer,"r");fsetpos(fp,&get_pos);if (fp != NULL){char max_line[LINE_MAX];while(fgets(max_line,sizeof(max_line),fp) != NULL){if (strstr(max_line,finder)||strstr(max_line,finder2)){fgetpos(fp,&get_pos);capture(max_line);}} fclose(fp);} else{ perror(tailer); } sleep(10);}return 0;}
二: capture函數
主要是一個中介層,把具體的正則式和匹配實現抽象成一個函數,供main使用。
很新奇的一點是,全部case使用了FALL THROUTH,又從regex_roller開始控制,很方便,省去了許多修改功能的注釋時間,不需要某個引擎又不想要刪除的時候,直接放在regex_roller的前面即可。
#define att_1 "((\%3C)|<)[^\n]+((\%3E)|>)"
#define att_2 "((\%3C)|<)((\%2F)|\\/)*[a-z0-9\%]+((\%3E)|>)"
#define att_3 "((\%3C)|<)((\%69)|i|(\%49))((\%6D)|m|(\%4D))((\%67)|g|(\%47))[^\n]+((\%3E)|>)"
#define att_4 "((\%27)|('))union"
#define att_5 "((\%3D)|(=))[^\n]*((\%27)|(\\')|(\\-\\-)|(\%3B)|(;))"
#define att_6 "((\%27)|('))"
char *capture(char *log_line){int regex_roller =0;char *xss_para_regex = att_1;char *xss_simple_regex = att_2;char *css_img_regex = att_3;char *unionsql_regex = att_4;char *sqlmeta_regex = att_5;char *sqlmagicquote_regex = att_6;switch(regex_roller){//add as many more as you wish but dont forget to #define the regex above.case 0:cap_matcher(log_line,xss_para_regex,0);case 1:cap_matcher(log_line,xss_simple_regex,1);case 2:cap_matcher(log_line,css_img_regex,2);case 3:cap_matcher(log_line,unionsql_regex,3);case 4:cap_matcher(log_line,sqlmeta_regex,4);case 5:cap_matcher(log_line,sqlmagicquote_regex,5);default:break;}return 0;}
方便帶來的後果就是如果檢測出一段攻擊代碼後,會對這段代碼繼續進行其他可能性的檢測,這是不需要的,同時底層函數功能過於複雜,把本該在中介層函數實現的功能帶到了底層去。
三: 匹配引擎
首先編譯正則式,然後進行匹配,匹配成功,根據傳入的規則編號,輸出對應的攻擊方式,阻塞IP,再利用郵件通知給管理員。
char *cap_matcher(char *log_line, char *regex,int attack_type){pid_t pid;pcre *attack_regex;const char *error;int erroffset;int ovector[OVECCOUNT];int rc;attack_regex = pcre_compile(regex,0,&error,&erroffset,NULL);if (! attack_regex){fprintf(stderr,"PCRE compilation failed at expression offset %d: %s\n", erroffset, error);return (char *)1;}rc = pcre_exec(attack_regex,NULL,log_line,strlen(log_line),0,0,ovector,OVECCOUNT);if (rc < 3){return (char *)1;}else{switch(attack_type){case 0:printf("Paranoid Xss Filter Detection\n");iptables_blockage(log_line);break;case 1:printf("Simple Xss Filter Detection\n");iptables_blockage(log_line);break;case 2:printf("Xss Img Filter Detection\n");iptables_blockage(log_line);break;case 3:printf("Sql Injection Union Filter Detection\n");iptables_blockage(log_line);break;case 4:printf("Sql Injection meta characters Filter Detection\n");iptables_blockage(log_line);break;case 5:printf("Sql Injection magic quote Filter Detection\n");iptables_blockage(log_line);break;default:break;}pid = fork();if (pid ==0){ FILE *emails = popen("/usr/bin/mail -s 'WebAttack On server' root@localhost","w"); fprintf(emails,"Attack FOUND %s ! in the log file.\n",log_line);pclose(emails);_exit(0);} }return 0;}
個人感覺作者在這裡的代碼邏輯有些混亂,應該在匹配成功後,結束代碼,返回中介層的capture函數進行處理,在capture中用宏來替換代碼
這樣代碼會少很多而且邏輯清晰:)
#define R(re,way,info) if(cap_matcher(log_line,#re,#way)){\ printf(##info);\iptables_blockage(log_line);\break;\}
四: 防火牆阻塞IP
apache的log檔案如下格式:
127.0.0.1 - - [23/Sep/2011:15:27:36 +0800] "GET / HTTP/1.1" 200 44
因此以第一個空格為標準,擷取IP後,調用iptables添加阻塞IP
void iptables_blockage(char *log_line){char *ip_address= malloc(100);char command[1000];int i;for (i =0; i <= 100 ; i++){if (isspace(log_line[i])){break;}ip_address[i] = log_line[i];}snprintf(command,sizeof(command),"/sbin/iptables -A INPUT -s %s -j DROP",ip_address);FILE *iptables_run = (FILE*)popen(command,"r");pclose(iptables_run);free(ip_address);}
最後的郵件通知實現與iptables差不多,詳細的可以參見前一篇文章,LINUX下C語言利用命令發郵件