從escapeshellcmd bypass說起到寬字節注入

1 php 多字節繞過escapeshellcmd

escapeshellcmd()對shell元字符過濾加反斜槓; 
反斜線(\)會在以下字符之前插入: #&;`|*?~<>^()[]{}$, \x0A 和 \xFF,但在php5.2.5及之前存在通過輸入多字節繞過escapeshellcmd的問題。5.2.6 已經修復了該問題。

執行 escapeshellcmd("echo ".chr(0xc0).";id"); 
加上反斜槓之後,也就是echo \xc0\x5c;id,在中文環境中\xc0\x5c是會被認爲是gbk字符的。


  1. >>> hex(ord('\\'))

  2. '0x5c'

  3. >>> s='\xc0\x5c'

  4. >>> print s.decode('gbk').encode('utf8')

  5. >>> s.decode('gbk').encode('utf8')

  6. '\xe7\xb9\xba'

\被吃掉之後於是就變成了echo 繺;id 了。 
gbk是寬字節,兩個字節,gbk字符範圍:8140-FEFE,首字節在81-FE直接,尾字節在40-FE之間,顯然5C在尾字節中。考慮0xbf;id,escape之後就變成了0xbf5c;id,0xbf5c是一個合法的GBK編碼,那就變成了[0xbf5c];id了。而utf8表示中文一般三個字節。 
同樣受影響的還有escapeshellarg(),源碼中的處理是一個字節一個字節來處理的。這種漏洞應該有一定普遍性,在當時來說。下面我們看下修復的源代碼:


  1. char *php_escape_shell_cmd(char *str) {

  2. register int x, y, l;

  3. char *cmd;

  4. char *p = NULL;

  5. TSRMLS_FETCH();

  6. l = strlen(str);

  7. cmd = safe_emalloc(2, l, 1); //申請了2倍字符

  8. for (x = 0, y = 0; x < l; x++) {

  9. int mb_len = php_mblen(str + x, (l - x));

  10. //這一段是5.2.6新加的,就是在處理多字節符號的時候,當多字節字符小於0的時候不處理,大於1的時候跳過,等於1的時候執行過濾動作

  11. /* skip non-valid multibyte characters */

  12. if (mb_len < 0) {

  13. continue;

  14. } else if (mb_len > 1) {

  15. memcpy(cmd + y, str + x, mb_len);

  16. y += mb_len;

  17. x += mb_len - 1;

  18. continue;

  19. }

  20. switch (str[x]) {

  21. case '"':

  22. case '\'':

  23. #ifndef PHP_WIN32

  24. if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {

  25. /* noop */

  26. } else if (p && *p == str[x]) {

  27. p = NULL;

  28. } else {

  29. cmd[y++] = '\\';

  30. }

  31. cmd[y++] = str[x];

  32. break;

  33. #endif

  34. case '#': /* This is character-set independent */

  35. case '&':

  36. case ';':

  37. case '`':

  38. case '|':

  39. case '*':

  40. case '?':

  41. case '~':

  42. case '<':

  43. case '>':

  44. case '^':

  45. case '(':

  46. case ')':

  47. case '[':

  48. case ']':

  49. case '{':

  50. case '}':

  51. case '$':

  52. case '\\':

  53. case '\x0A': /* excluding these two */

  54. case '\xFF':

  55. #ifdef PHP_WIN32

  56. /* since Windows does not allow us to escape these chars, just remove them */

  57. case '%':

  58. cmd[y++] = ' ';

  59. break;

  60. #endif

  61. cmd[y++] = '\\';

  62. /* fall-through */

  63. default:

  64. cmd[y++] = str[x];

  65. }

  66. }

  67. cmd[y] = '\0';

  68. return cmd;

  69. }

這個bypass已經成爲過去時了,但是還是有很大的借鑑意義,就是寬字節注入,這種情況不僅僅發生命令注入時,更多的時候在sql注入,下面來分析一下寬字節注入如下三種情況,都是由於寬字節的問題導致的。

2 寬字節sql注入

1,一種情況 iconv轉換,addslashes之後從gbk轉到utf8


  1. $user = $_POST[ 'username' ];

  2. $user = addslashes($user);

  3. $user = iconv("gbk", 'utf8', $user);

  4. $pass = $_POST[ 'password' ];

  5. $pass = md5( $pass );

  6. $qry = "SELECT * FROM `users` WHERE user='$user' AND password='$pass';";

  7. print_r($qry);

  8. $result = @mysql_query($qry) or die('<pre>' . mysql_error() . '</pre>' );

  9. var_dump($result);

處理過程如下: 
%bf%27----(addslashes)->%bf%5c%27-----(utf8)---->縗' 這樣單引號就放出來了,大體流程是%bf%27經過addslashes之後變成了%bf%5c%27,再經過iconv從gbk轉換爲utf8的時候,變成了%e7%b8%97%27,也就是縗'。利用的前提是設置了set names utf8。

2,在php中使用mysql_query('set names gbk'),指定了客戶端,連接層,結果爲gbk編碼。構造數據%bf%27,過程和第一種情況類似 
%bf%27---(addslashes)-->%bf%5c%27---(set names gbk)--->縗'

3,iconv轉換從utf8到gbk,set names字符集爲gbk,構造數據如下%e9%8c%a6帶入反斜槓,註釋掉單引號 
大體數據流程:%e9%8c%a6-----(utf8)----%e5%5c----(addslashes)--->%e5%5c%5c


  1. >>> s = '\xe9\x8c\xa6'

  2. >>> s.decode('utf8')

  3. u'\u9326'

  4. >>> s.decode('utf8').encode('gbk')

  5. '\xe5\\'

總之一條,都是打的%5c的注意,要麼轉義後轉utf8吃掉%5c,要麼轉utf8後再轉義放出%5c

參考: 
http://seclists.org/bugtraq/2008/May/61 
http://www.sektioneins.de/en/advisories/advisory-032008-php-multibyte-shell-command-escaping-bypass-vulnerability.html 
http://php.net/ChangeLog-5.php 
http://php.net/releases/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章