1<?php 2/******************************************************************************* 3** Basic Analysis and Security Engine (BASE) 4** Copyright (C) 2004 BASE Project Team 5** Copyright (C) 2000 Carnegie Mellon University 6** 7** (see the file 'base_main.php' for license details) 8** 9** Project Leads: Kevin Johnson <kjohnson@secureideas.net> 10** Sean Muller <samwise_diver@users.sourceforge.net> 11** Built upon work by Roman Danyliw <rdd@cert.org>, <roman@danyliw.com> 12** 13** Purpose: Binary download of payload and pcap format packet download 14** 15** Input GET/POST variables 16** - sid 17** - cid 18** - download: 1 - binary download of just the payload. Nothing else. 19** 2 - download pcap format based packet (for FLoP extended db) 20** 3 - download pcap format based packet (for non-FLoP) 21******************************************************************************** 22** Authors: 23******************************************************************************** 24** Kevin Johnson <kjohnson@secureideas.net> 25** 26******************************************************************************** 27*/ 28 29include ("base_conf.php"); 30include ("$BASE_path/includes/base_constants.inc.php"); 31include ("$BASE_path/includes/base_include.inc.php"); 32 33// Check role out and redirect if needed -- Kevin 34$roleneeded = 10000; 35$BUser = new BaseUser(); 36if (($BUser->hasRole($roleneeded) == 0) && ($Use_Auth_System == 1)) 37{ 38 base_header("Location: ". $BASE_urlpath . "/index.php"); 39 exit(); 40} 41 42$cid = ImportHTTPVar("cid", VAR_DIGIT); 43$sid = ImportHTTPVar("sid", VAR_DIGIT); 44$download = ImportHTTPVar("download", VAR_DIGIT); 45 46 47/**********************************************************/ 48/* 1 = binary download of just the payload. Nothing else. */ 49if ($download == 1) 50{ 51 /* Connect to the Alert database */ 52 $db = NewBASEDBConnection($DBlib_path, $DBtype); 53 $db->baseDBConnect($db_connect_method, 54 $alert_dbname, $alert_host, $alert_port, $alert_user, $alert_password); 55 56 /* Get the Payload from the database: */ 57 $sql2 = "SELECT data_payload FROM data WHERE sid='".$sid."' AND cid='".$cid."'"; 58 $result2 = $db->baseExecute($sql2); 59 $myrow2 = $result2->baseFetchRow(); 60 $result2->baseFreeRows(); 61 62 /* get encoding information for payload */ 63 /* 0 == hex, 1 == base64, 2 == ascii; */ 64 $sql3 = 'SELECT encoding FROM sensor WHERE sid='.$sid; 65 $result3 = $db->baseExecute($sql3); 66 $myrow3 = $result3->baseFetchRow(); 67 $result3->baseFreeRows(); 68 69 if ( $myrow2[0] ){ 70 /****** database contains hexadecimal *******************/ 71 if ($myrow3[0] == 0){ 72 header ('HTTP/1.0 200'); 73 header ("Content-type: application/download"); 74 header ("Content-Disposition: attachment; filename=payload_".$sid."-".$cid.".bin"); 75 header ("Content-Transfer-Encoding: binary"); 76 ob_start(); 77 $payload = str_replace("\n", "", $myrow2[0]); 78 $len = strlen($payload); 79 $half = ($len / 2); 80 header ("Content-Length: $half"); 81 $counter = 0; 82 for ($i = 0; $i < ( $len + 32 ); $i += 2){ 83 $counter++; 84 if ($counter > ($len / 2)){ 85 break; 86 } 87 $byte_hex_representation = ($payload[$i].$payload[$i+1]); 88 echo chr(hexdec($byte_hex_representation)); 89 } 90 ob_end_flush(); 91 // nothing should come AFTER ob_end_flush(). 92 93 /********database contains base64 *******************/ 94 } elseif ($myrow3[0] == 1){ 95 header ('HTTP/1.0 200'); 96 header ("Content-type: application/octet-stream"); 97 header ("Content-Disposition: attachment; filename=payload".$sid."-".$cid.".bin"); 98 header ("Content-Transfer-Encoding: binary"); 99 ob_start(); 100 $pre_payload = str_replace("\n", "", $myrow2[0]); 101 $payload = base64_decode($pre_payload); 102 $len = strlen($payload); 103 header ("Content-Length: $len"); 104 $counter = 0; 105 for ($i = 0; $i < ($len + 16); $i++){ 106 $counter++; 107 if ($counter > $len) { 108 break; 109 } 110 $byte = $payload[$i]; 111 print $byte; 112 } 113 ob_end_flush(); 114 // nothing should come AFTER ob_end_flush(). 115 116 /********** database contains ASCII ***************/ 117 } elseif ($myrow3[0] == 2){ 118 header ('HTTP/1.0 200'); 119 header ('Content-Type: text/html'); 120 print "<h1> File not found:</h1>"; 121 122 print "<br>Output of binary data with storage method ASCII<br>"; 123 print "is NOT supported, because this method looses data<br>"; 124 print "So you can not definitely rebuild the binary,<br>"; 125 print "as one ASCII character may represent different<br>"; 126 print "binary values. Think of the dot, for example.<br>"; 127 128 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 129 } else { 130 header ('HTTP/1.0 200'); 131 header ('Content-Type: text/html'); 132 print "<h1> File not found:</h1>"; 133 print "<br>Encoding type not implemented in base_payload.php."; 134 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 135 } 136 } else { 137 header ('HTTP/1.0 200'); 138 header ('Content-Type: text/html'); 139 print "<h1> File not found:</h1>"; 140 print "<br>No payload data found, that could be downloaded or stored."; 141 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 142 } 143 144} 145/**************************************************************************/ 146/* pcap download, for both flop-extended databases and non-flop databases */ 147else if ($download == 2 || $download == 3) 148{ 149 /* 150 * If we have FLoP extended database schema then we can rebuild alert 151 * in pcap format which can be used to analyze it via tcpdump or 152 * ethereal to use their protocol analyzing features. 153 */ 154 155 /* Connect to the Alert database. */ 156 $db = NewBASEDBConnection($DBlib_path, $DBtype); 157 $db->baseDBConnect($db_connect_method, 158 $alert_dbname, $alert_host, $alert_port, $alert_user, $alert_password); 159 160 161 /* Sanity check */ 162 if ($download == 2) 163 { 164 /* Check do we have pcap_header and data_header columns in data table. */ 165 if (!in_array("pcap_header", $db->DB->MetaColumnNames('data')) || 166 !in_array("data_header", $db->DB->MetaColumnNames('data'))) 167 { 168 header ('HTTP/1.0 200'); 169 header ('Content-Type: text/html'); 170 print "<h1> File not found:</h1>"; 171 print "<br>Make sure you have FLoP extended database."; 172 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 173 exit; 174 } 175 } 176 177 178 179 /*********** Get needed data from database. **************/ 180 181 /* For FLoP-extended databases: */ 182 if ($download == 2) 183 { 184 $sql2 = "SELECT pcap_header, data_header, data_payload FROM data "; 185 $sql2.= "WHERE sid='".$sid."' AND cid='".$cid."'"; 186 } 187 /* For non-flop databases: */ 188 else 189 { 190 $sql2 = "SELECT data_payload FROM data "; 191 $sql2.= "WHERE sid='".$sid."' AND cid='".$cid."'"; 192 } 193 $result2 = $db->baseExecute($sql2); 194 $myrow2 = $result2->baseFetchRow(); 195 $result2->baseFreeRows(); 196 197 /* Get encoding information for current sensor. */ 198 $sql3 = 'SELECT encoding FROM sensor WHERE sid='.$sid; 199 $result3 = $db->baseExecute($sql3); 200 $myrow3 = $result3->baseFetchRow(); 201 $result3->baseFreeRows(); 202 203 204 205 /* For flop-extended databases: IP header information is already present 206 in myrow2. 207 208 For non-flop databases we are NOT done, yet. So, try and get the missing 209 information: */ 210 if ($download == 3) 211 { 212 $ip_sql = "SELECT ip_ver, ip_hlen, ip_tos, ip_len, ip_id, ip_off, ip_flags,"; 213 $ip_sql.= "ip_ttl, ip_proto, ip_csum, ip_src, ip_dst FROM iphdr "; 214 $ip_sql.= "WHERE sid='".$sid."' AND cid='".$cid."'"; 215 $ip_res = $db->baseExecute($ip_sql); 216 $ip = $ip_res->baseFetchRow(); 217 $ip_res->baseFreeRows(); 218 219 if ($ip[8] == 1) 220 { 221 $l4_sql = "SELECT icmp_type, icmp_code, icmp_csum, icmp_id, icmp_seq "; 222 $l4_sql.= "FROM icmphdr WHERE sid='".$sid."' AND cid='".$cid."'"; 223 } 224 elseif ($ip[8] == 6) 225 { 226 $l4_sql = "SELECT tcp_sport, tcp_dport, tcp_seq, tcp_ack, tcp_off,"; 227 $l4_sql.= "tcp_res, tcp_flags, tcp_win, tcp_csum, tcp_urp from tcphdr "; 228 $l4_sql.= "WHERE sid='".$sid."' AND cid='".$cid."'"; 229 } 230 elseif ($ip[8] == 17) 231 { 232 $l4_sql = "SELECT udp_sport, udp_dport, udp_len, udp_csum FROM udphdr "; 233 $l4_sql.= "WHERE sid='".$sid."' AND cid='".$cid."'"; 234 } 235 236 $l4_res = $db->baseExecute($l4_sql); 237 $l4 = $l4_res->baseFetchRow(); 238 $l4_res->baseFreeRows(); 239 240 } // if ($download == 3) 241 242 243 /***********************************************************************/ 244 /* Now, when extracting the information from the database: Pay attention 245 * to the encoding: 246 * 247 /* 0 == hex, 1 == base64, 2 == ascii; cf. snort-2.8.5.1/src/plugbase.h */ 248 249 /****** hexadecimal encoding *********/ 250 if ($myrow3[0] == 0) 251 { 252 /* FLoP-extended databases */ 253 if ($download == 2) 254 { 255 $pcap_header = $myrow2[0]; 256 $data_header = $myrow2[1]; 257 $data_payload = $myrow2[2]; 258 } 259 /* Non-flop databases */ 260 else 261 { 262 $data_payload = $myrow2[0]; 263 } 264 } 265 /******** base64 encoding ********/ 266 elseif ($myrow3[0] == 1) 267 { 268 /* FLoP-extended databases */ 269 if ($download == 2) 270 { 271 $pcap_header = bin2hex(base64_decode($myrow2[0])); 272 $data_header = bin2hex(base64_decode($myrow2[1])); 273 $data_payload = bin2hex(base64_decode($myrow2[2])); 274 } 275 /* Non-flop databases */ 276 else 277 { 278 $data_payload = bin2hex(base64_decode($myrow2[0])); 279 } 280 } 281 else 282 { 283 /******* database contains neither hex nor base64 encoding. *********/ 284 header ('HTTP/1.0 200'); 285 header ('Content-Type: text/html'); 286 print "<h1> File not found:</h1>"; 287 print "<br>Only HEX and BASE64 encoding types are supported, nothing else."; 288 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 289 exit; 290 } 291 292 293 294 295 /* 296 * From here on: pcap header, data_header and data_payload all contain data 297 * in hex encoding, even if original encoding type was base64. 298 */ 299 300 /* Sanity checks for FLoP-extended databases*/ 301 if ($download == 2) 302 { 303 # /usr/include/pcap.h: 304 # struct pcap_pkthdr { 305 # struct timeval ts; /* time stamp */ 306 # bpf_u_int32 caplen; /* length of portion present */ 307 # bpf_u_int32 len; /* length this packet (off wire) */ 308 # }; 309 # 310 # And a struct timeval has either a 32-bit or 64-bit tv_sec. 311 if (strlen($pcap_header) > 48) 312 { 313 header ('HTTP/1.0 200'); 314 header ('Content-Type: text/html'); 315 print "<h1> File not found:</h1>"; 316 print "<br>Error in pcap_header, answer is too large: ".strlen($pcap_header)."!"; 317 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 318 exit; 319 } 320 else if (strlen($pcap_header) == 0) 321 { 322 header ('HTTP/1.0 200'); 323 header ('Content-Type: text/html'); 324 print "<h1> File not found:</h1>"; 325 print "<br>No pcap header, we can't rebuild the network packet."; 326 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 327 exit; 328 } 329 } // if ($download == 2) 330 331 332 333 334 335 /* For non-flop databases, the data_header has not been created, yet. 336 * Do it now: */ 337 if ($download == 3) 338 { 339 # tack an ethernet header on there 340 $data_header = "DEADCAFEBABE1122334455660800"; 341 342 # later on, all of this gets interpreted as hex, so simply 343 # pull the values from the db, convert them to hex, 0-pad them 344 # as necessary, and tack them together. 345 $data_header.= sprintf("%02s", $ip[0] . $ip[1]); // ver&ihl 346 $data_header.= sprintf("%02s", dechex($ip[2])); // tos 347 $data_header.= sprintf("%04s", dechex($ip[3])); // len 348 $data_header.= sprintf("%04s", dechex($ip[4])); // id 349 $data_header.= sprintf("%02s", dechex($ip[5])); // flags 350 $data_header.= sprintf("%02s", dechex($ip[6])); // offset 351 $data_header.= sprintf("%02s", dechex($ip[7])); // ttl 352 $data_header.= sprintf("%02s", dechex($ip[8])); // proto 353 $data_header.= sprintf("%04s", dechex($ip[9])); // csum. 354 355 # http://us2.php.net/manual/en/function.dechex.php#71795 356 # source IP 357 $chars = ($ip[10] <= 0x0fffffff) ? 1 : 0; 358 $data_header.= sprintf("%02s", substr(dechex((float) $ip[10]),0,2-$chars)); 359 360 for ($i = 1; $i < 4; $i++) 361 $data_header.= sprintf("%02s", substr(dechex((float) $ip[10]), $i*2-$chars, 2)); 362 363 # dest IP 364 $chars = ($ip[11] <= 0x0fffffff) ? 1 : 0; 365 $data_header.= sprintf("%02s", substr(dechex((float) $ip[11]),0,2-$chars)); 366 367 for ($i = 1; $i < 4; $i++) 368 $data_header.= sprintf("%02s", substr(dechex((float) $ip[11]), $i*2-$chars, 2)); 369 370 if ($ip[8] == 1) 371 { 372 $data_header.= sprintf("%02s", dechex((float) $l4[0])); // type 373 $data_header.= sprintf("%02s", dechex((float) $l4[1])); // code 374 $data_header.= sprintf("%04s", dechex((float) $l4[2])); // sum 375 // only echo req/rep, timestamp, info req/rep have id/seq 376 if ($l4[0] == 0 || $l4[0] == 8 || ($l4[0] >= 13 && $l4[0] <= 16)) 377 { 378 $data_header.= sprintf("%04s", dechex((float) $l4[3])); // id 379 $data_header.= sprintf("%04s", dechex((float) $l4[4])); // seq 380 } 381 } 382 elseif ($ip[8] == 6) 383 { 384 $data_header.= sprintf("%04s", dechex((float) $l4[0])); // source port 385 $data_header.= sprintf("%04s", dechex((float) $l4[1])); // dest port 386 $data_header.= sprintf("%08s", dechex((float) $l4[2])); // seq # 387 $data_header.= sprintf("%08s", dechex((float) $l4[3])); // ack # 388 $data_header.= sprintf("%01s", dechex((float) $l4[4])); // offset 389 $data_header.= sprintf("%03s", dechex((float) $l4[6])); // flags 390 $data_header.= sprintf("%04s", dechex((float) $l4[7])); // window 391 $data_header.= sprintf("%04s", dechex((float) $l4[8])); // checksum 392 $data_header.= sprintf("%04s", dechex((float) $l4[9])); // urg ptr 393 394 # walk opts... 395 $tcp_opt_sql = "SELECT optid, opt_code, opt_len, opt_data FROM opt "; 396 $tcp_opt_sql.= "WHERE sid='".$sid."' AND cid='".$cid."' AND opt_proto=6 ORDER BY optid ASC"; 397 $tcp_opt_res = $db->baseExecute($tcp_opt_sql); 398 $tcp_opt_data = ""; 399 400 401 while ($tcp_opt = $tcp_opt_res->baseFetchRow()) 402 { 403 $tcp_opt_data .= sprintf("%02s", dechex((float) $tcp_opt[1])); 404 405 // if opt_len == 0, its an "opt kind", and thus has no length or data 406 if ($tcp_opt[2] != 0) 407 { 408 $tcp_opt_data .= sprintf("%02s", dechex((float) $tcp_opt[2] + 2)); 409 $tcp_opt_data .= $tcp_opt[3]; 410 } 411 } // while ($tcp_opt = $tcp_opt_res->baseFetchRow()) 412 413 $tcp_opt_res->baseFreeRows(); 414 $data_header.= $tcp_opt_data; 415 416 } 417 elseif ($ip[8] == 17) 418 { 419 $data_header.= sprintf("%04s", dechex((float) $l4[0])); // source port 420 $data_header.= sprintf("%04s", dechex((float) $l4[1])); // dest port 421 $data_header.= sprintf("%04s", dechex((float) $l4[2])); // len 422 $data_header.= sprintf("%04s", dechex((float) $l4[3])); // sum 423 } 424 425 } // if ($download == 3) 426 427 428 429 430 /*****************************************************************/ 431 /* Now, begin to create the file the user wants to download: */ 432 header ('HTTP/1.0 200'); 433 header ("Content-type: application/octet-stream"); 434 header ("Content-Disposition: attachment; filename=base_packet_".$sid."-".$cid.".pcap"); 435 header ("Content-Transfer-Encoding: binary"); 436 437 438 439 /* 440 * Calculating snaplen which is length of payload plus header, 441 * for HEX we have to divide by two -> two HEX characters 442 * represent one binary byte. 443 */ 444 445 $snaplen = (strlen($data_header) + strlen($data_payload)) / 2; 446 header ("Content-length: ". 40 + $snaplen); 447 448 449 450 /* Create pcap file header. */ 451 $hdr['magic'] = pack('L', 0xa1b2c3d4); /* unsigned long (always 32 bit, machine byte order) */ 452 $hdr['version_major'] = pack('S', 2); /* unsigned short (always 16 bit, machine byte order) */ 453 $hdr['version_minor'] = pack('S', 4); /* unsigned short (always 16 bit, machine byte order) */ 454 $hdr['thiszone'] = pack('I', 0); /* signed long (always 32 bit, machine byte order) */ 455 $hdr['sigfigs'] = pack('L', 0); /* unsigned long (always 32 bit, machine byte order) */ 456 $hdr['snaplen'] = pack('L', $snaplen); /* unsigned long (always 32 bit, machine byte order) */ 457 $hdr['linktype'] = pack('L', 1); /* unsigned long (always 32 bit, machine byte order) */ 458 459 460 /* Create pcap packet header. Converting hex to decimal and then to network byte order (big endian). */ 461 /* For FLoP-extended databases: */ 462 if ($download == 2) 463 { 464 /* tv_sec in a struct timeval is either 32 bits or 64 bits long. 465 * But, as it seems, in $pcap_header it is ALWAYS 32 bits = 4 bytes long. 466 * Which means that, in the way as snort stores it, 8 bytes are consumed. 467 * So, offset is 0, and length is ALWAYS 8. 468 * I have not checked whether this is FLoP's or snort's fault. 469 */ 470 list(, $phdr['timeval_sec']) = unpack('L', pack('N', hexdec(substr($pcap_header, 0, 8)))); 471 472 if (strlen($pcap_header) > 32) 473 { 474 /* A 64-bit tv_sec in a struct timeval are 8 bytes. In hexadecimal form 475 as snort stores it, this consumes 16 bytes */ 476 list(, $phdr['timeval_usec']) = unpack('L', pack('N', hexdec(substr($pcap_header, 16, 8)))); 477 } 478 else 479 { 480 /* A 32-bit tv_sec in a struct timeval are 4 bytes. In hexadecimal form 481 as snort stores it, this consumes 8 bytes */ 482 list(, $phdr['timeval_usec']) = unpack('L', pack('N', hexdec(substr($pcap_header, 8, 8)))); 483 } 484 485 list(, $phdr['caplen']) = unpack('L', pack('N', hexdec(substr($pcap_header, (strlen($pcap_header)) - 16, 8)))); 486 list(, $phdr['len']) = unpack('L', pack('N', hexdec(substr($pcap_header, strlen($pcap_header) - 8, 8)))); 487 488 489 if ($debug_mode > 0) 490 { 491 error_log("phdr[timeval_sec] = \"" . $phdr['timeval_sec'] . "\""); 492 error_log("phdr[timeval_usec] = \"" . $phdr['timeval_usec'] . "\""); 493 error_log("snaplen = $snaplen bytes.<BR>\n"); 494 error_log("phdr[caplen] = \"" . $phdr['caplen'] . "\""); 495 list(, $tmp['caplen_new']) = unpack('L', pack('N', hexdec(substr($pcap_header, 32, 8)))); 496 error_log("phdr[caplen] new = \"" . $tmp['caplen_new'] . "\""); 497 error_log("phdr[len] = \"" . $phdr['len'] . "\""); 498 list(, $tmp['len_new']) = unpack('L', pack('N', hexdec(substr($pcap_header, 40, 8)))); 499 error_log("phdr[len] new = \"" . $tmp['len_new'] . "\""); 500 } 501 } 502 /* For non-flop databases */ 503 else 504 { 505 $ts_sql = "SELECT timestamp FROM event "; 506 $ts_sql.= "WHERE sid='".$sid."' AND cid='".$cid."'"; 507 $ts_res = $db->baseExecute($ts_sql); 508 $ts_string = $ts_res->baseFetchRow(); 509 $ts_res->baseFreeRows(); 510 $ts = strtotime($ts_string[0]); 511 list(, $phdr['timeval_sec']) = unpack('L', pack('L', $ts)); 512 list(, $phdr['timeval_usec']) = unpack('L', pack('L', 0)); 513 list(, $phdr['caplen']) = unpack('L', pack('L', $snaplen)); 514 list(, $phdr['len']) = unpack('L', pack('L', $snaplen)); 515 } 516 517 /* Copy header to packet, convert hex to dec and from dec to char. */ 518 $packet = ""; 519 for ($i = 0; $i < strlen($data_header); $i = $i + 2) 520 $packet .= chr(hexdec(substr($data_header, $i, 2))); 521 522 /* Copy payload to packet, convert hex to dec and from dec to char. */ 523 for ($i = 0; $i < strlen($data_payload); $i = $i + 2) 524 $packet .= chr(hexdec(substr($data_payload, $i, 2))); 525 526 ob_start(); 527 528 /* Writing pcap file header */ 529 foreach ($hdr as $value) 530 echo $value; 531 532 /* Writing pcap packet header */ 533 foreach ($phdr as $value) 534 echo pack('L', $value); 535 536 /* Writing packet */ 537 echo $packet; 538 539 ob_end_flush(); 540 /* nothing should come after ob_end_flush(). */ 541} 542else // else if ($download == 2 || $download == 3) 543{ 544 header ('HTTP/1.0 200'); 545 header ('Content-Type: text/html'); 546 print "<h1> File not found:</h1>"; 547 print "<br>This page is only intended for downloading purposes; it has no content."; 548 print "<br><br><hr><i>Generated by base_payload.php</i><br>"; 549} // if ($download == 1) 550?> 551