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