1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <assert.h>
5 
6 #include "libinjection.h"
7 #include "libinjection_sqli.h"
8 #include "libinjection_xss.h"
9 
10 #ifndef TRUE
11 #define TRUE 1
12 #endif
13 #ifndef FALSE
14 #define FALSE 0
15 #endif
16 
17 static int g_test_ok = 0;
18 static int g_test_fail = 0;
19 
20 typedef enum {
21     MODE_SQLI,
22     MODE_XSS
23 } detect_mode_t;
24 
25 static void usage(const char* argv[]);
26 size_t modp_rtrim(char* str, size_t len);
27 void modp_toprint(char* str, size_t len);
28 void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
29                    int flag_invert, int flag_true, int flag_quiet);
30 
31 int urlcharmap(char ch);
32 size_t modp_url_decode(char* dest, const char* s, size_t len);
33 
urlcharmap(char ch)34 int urlcharmap(char ch) {
35     switch (ch) {
36     case '0': return 0;
37     case '1': return 1;
38     case '2': return 2;
39     case '3': return 3;
40     case '4': return 4;
41     case '5': return 5;
42     case '6': return 6;
43     case '7': return 7;
44     case '8': return 8;
45     case '9': return 9;
46     case 'a': case 'A': return 10;
47     case 'b': case 'B': return 11;
48     case 'c': case 'C': return 12;
49     case 'd': case 'D': return 13;
50     case 'e': case 'E': return 14;
51     case 'f': case 'F': return 15;
52     default:
53         return 256;
54     }
55 }
56 
modp_url_decode(char * dest,const char * s,size_t len)57 size_t modp_url_decode(char* dest, const char* s, size_t len)
58 {
59     const char* deststart = dest;
60 
61     size_t i = 0;
62     int d = 0;
63     while (i < len) {
64         switch (s[i]) {
65         case '+':
66             *dest++ = ' ';
67             i += 1;
68             break;
69         case '%':
70             if (i+2 < len) {
71                 d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
72                 if ( d < 256) {
73                     *dest = (char) d;
74                     dest++;
75                     i += 3; /* loop will increment one time */
76                 } else {
77                     *dest++ = '%';
78                     i += 1;
79                 }
80             } else {
81                 *dest++ = '%';
82                 i += 1;
83             }
84             break;
85         default:
86             *dest++ = s[i];
87             i += 1;
88         }
89     }
90     *dest = '\0';
91     return (size_t)(dest - deststart); /* compute "strlen" of dest */
92 }
93 
modp_toprint(char * str,size_t len)94 void modp_toprint(char* str, size_t len)
95 {
96     size_t i;
97     for (i = 0; i < len; ++i) {
98         if (str[i] < 32 || str[i] > 126) {
99             str[i] = '?';
100         }
101     }
102 }
modp_rtrim(char * str,size_t len)103 size_t modp_rtrim(char* str, size_t len)
104 {
105     while (len) {
106         char c = str[len -1];
107         if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
108             str[len -1] = '\0';
109             len -= 1;
110         } else {
111             break;
112         }
113     }
114     return len;
115 }
116 
test_positive(FILE * fd,const char * fname,detect_mode_t mode,int flag_invert,int flag_true,int flag_quiet)117 void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
118                    int flag_invert, int flag_true, int flag_quiet)
119 {
120     char linebuf[8192];
121     int issqli = 0;
122     int linenum = 0;
123     size_t len;
124     sfilter sf;
125 
126     while (fgets(linebuf, sizeof(linebuf), fd)) {
127         linenum += 1;
128         len = modp_rtrim(linebuf, strlen(linebuf));
129         if (len == 0) {
130             continue;
131         }
132         if (linebuf[0] == '#') {
133             continue;
134         }
135 
136         len =  modp_url_decode(linebuf, linebuf, len);
137         switch (mode) {
138         case MODE_SQLI: {
139             libinjection_sqli_init(&sf, linebuf, len, 0);
140             issqli = libinjection_is_sqli(&sf);
141             break;
142         }
143         case MODE_XSS: {
144             issqli = libinjection_xss(linebuf, len);
145             break;
146         }
147         default:
148             assert(0);
149        }
150 
151         if (issqli) {
152             g_test_ok += 1;
153         } else {
154             g_test_fail += 1;
155         }
156 
157         if (!flag_quiet) {
158             if ((issqli && flag_true && ! flag_invert) ||
159                 (!issqli && flag_true && flag_invert) ||
160                 !flag_true) {
161 
162                 modp_toprint(linebuf, len);
163 
164                 switch (mode) {
165                 case MODE_SQLI: {
166 		    /*
167 		     * if we didn't find a SQLi and fingerprint from
168                      * sqlstats is is 'sns' or 'snsns' then redo using
169                      * plain context
170 		     */
171                     if (!issqli && (strcmp(sf.fingerprint, "sns") == 0 ||
172 				    strcmp(sf.fingerprint, "snsns") == 0)) {
173                         libinjection_sqli_fingerprint(&sf, 0);
174                     }
175 
176                     fprintf(stdout, "%s\t%d\t%s\t%s\t%s\n",
177                             fname, linenum,
178                             (issqli ? "True" : "False"), sf.fingerprint, linebuf);
179                     break;
180                 }
181                 case MODE_XSS: {
182                     fprintf(stdout, "%s\t%d\t%s\t%s\n",
183                             fname, linenum,
184                             (issqli ? "True" : "False"), linebuf);
185                     break;
186                 }
187                 default:
188                     assert(0);
189                 }
190             }
191         }
192     }
193 }
194 
usage(const char * argv[])195 static void usage(const char* argv[])
196 {
197   fprintf(stdout, "usage: %s [flags] [files...]\n", argv[0]);
198   fprintf(stdout, "%s\n", "");
199   fprintf(stdout, "%s\n", "-q --quiet     : quiet mode");
200   fprintf(stdout, "%s\n", "-m --max-fails : number of failed cases need to fail entire test");
201   fprintf(stdout, "%s\n", "-s INTEGER     : repeat each test N time "
202 	  "(for performance testing)");
203   fprintf(stdout, "%s\n", "-t             : only print positive matches");
204   fprintf(stdout, "%s\n", "-x --mode-xss  : test input for XSS");
205   fprintf(stdout, "%s\n", "-i --invert    : invert test logic "
206 	  "(input is tested for being safe)");
207 
208   fprintf(stdout, "%s\n", "");
209   fprintf(stdout, "%s\n", "-? -h -help --help : this page");
210   fprintf(stdout, "%s\n", "");
211 }
212 
main(int argc,const char * argv[])213 int main(int argc, const char *argv[])
214 {
215     /*
216      * invert output, by
217      */
218     int flag_invert = FALSE;
219 
220     /*
221      * don't print anything.. useful for
222      * performance monitors, gprof.
223      */
224     int flag_quiet = FALSE;
225 
226     /*
227      * only print positive results
228      * with invert, only print negative results
229      */
230     int flag_true = FALSE;
231     detect_mode_t mode = MODE_SQLI;
232 
233     int flag_slow = 1;
234     int count = 0;
235     int max = -1;
236 
237     int i, j;
238     int offset = 1;
239 
240     while (offset < argc) {
241         if (strcmp(argv[offset], "-?") == 0 ||
242             strcmp(argv[offset], "-h") == 0 ||
243 	    strcmp(argv[offset], "-help") == 0 ||
244 	    strcmp(argv[offset], "--help") == 0) {
245 	  usage(argv);
246 	  exit(0);
247 	}
248 
249         if (strcmp(argv[offset], "-i") == 0) {
250             offset += 1;
251             flag_invert = TRUE;
252         } else if (strcmp(argv[offset], "-q") == 0 ||
253 		   strcmp(argv[offset], "--quiet") == 0) {
254             offset += 1;
255             flag_quiet = TRUE;
256         } else if (strcmp(argv[offset], "-t") == 0) {
257             offset += 1;
258             flag_true = TRUE;
259         } else if (strcmp(argv[offset], "-s") == 0) {
260             offset += 1;
261             flag_slow = 100;
262         } else if (strcmp(argv[offset], "-m") == 0 ||
263 		   strcmp(argv[offset], "--max-fails") == 0) {
264 		     offset += 1;
265             max = atoi(argv[offset]);
266             offset += 1;
267         } else if (strcmp(argv[offset], "-x") == 0 ||
268 		   strcmp(argv[offset], "--mode-xss") == 0) {
269             mode = MODE_XSS;
270             offset += 1;
271         } else {
272             break;
273         }
274     }
275 
276     if (offset == argc) {
277         test_positive(stdin, "stdin", mode, flag_invert, flag_true, flag_quiet);
278     } else {
279         for (j = 0; j < flag_slow; ++j) {
280             for (i = offset; i < argc; ++i) {
281                 FILE* fd = fopen(argv[i], "r");
282                 if (fd) {
283                     test_positive(fd, argv[i], mode, flag_invert, flag_true, flag_quiet);
284                     fclose(fd);
285                 }
286             }
287         }
288     }
289 
290     if (!flag_quiet) {
291         fprintf(stdout, "%s", "\n");
292         fprintf(stdout, "SQLI  : %d\n", g_test_ok);
293         fprintf(stdout, "SAFE  : %d\n", g_test_fail);
294         fprintf(stdout, "TOTAL : %d\n", g_test_ok + g_test_fail);
295     }
296 
297     if (max == -1) {
298         return 0;
299     }
300 
301     count = g_test_ok;
302     if (flag_invert) {
303         count = g_test_fail;
304     }
305 
306     if (count > max) {
307         printf("\nThreshold is %d, got %d, failing.\n", max, count);
308         return 1;
309     } else {
310         printf("\nThreshold is %d, got %d, passing.\n", max, count);
311         return 0;
312     }
313 }
314