Linux

正常语句:

  1. http://127.0.0.1/a.jpg

攻击语句:

  1. http://127.0.0.1/a.jpg(非编码空格)\0.php

在对url的解析过程中,路径中存在’.’或url存在’\0’会有如下处理:

  1. #!cpp
  2. case sw_check_uri:
  3. ……
  4. case '.':
  5. r->complex_uri = 1; //此作为flag会判断使用ngx_http_parse_complex_uri方法,对路径修复
  6. state = sw_uri;
  7. break;
  8. casesw_check_uri:
  9. ……
  10. case '\0': //当遇到\0是,将会判断为非法字符
  11. return NGX_HTTP_PARSE_INVALID_REQUEST;

但是在检查uri中有空格则会进入到sw_check_uri_http_09的逻辑中,那么当我们发送攻击代码的时候,执行流程将如下:

  1. /* space+ after URI */
  2. case sw_check_uri_http_09:
  3. switch (ch) {
  4. case ' ':
  5. break;
  6. case CR:
  7. r->http_minor = 9;
  8. state = sw_almost_done;
  9. break;
  10. case LF:
  11. r->http_minor = 9;
  12. goto done;
  13. case 'H':
  14. r->http_protocol.data = p;
  15. state = sw_http_H;
  16. break;
  17. default:
  18. r->space_in_uri = 1;
  19. state = sw_check_uri;
  20. break;
  21. }
  22. break;

再回到sw_check_uri状态,此时后面的字符串为.php,而.将被为是uri的扩展名的分隔符

  1. case sw_check_uri:
  2. if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  3. break;
  4. }
  5. switch (ch) {
  6. case '/':
  7. r->uri_ext = NULL;
  8. state = sw_after_slash_in_uri;
  9. break;
  10. case '.':
  11. r->uri_ext = p + 1;
  12. break;
  13. case ' ':
  14. r->uri_end = p;
  15. state = sw_check_uri_http_09;
  16. break;
  17. case CR:
  18. r->uri_end = p;
  19. r->http_minor = 9;
  20. state = sw_almost_done;
  21. break;
  22. case LF:
  23. r->uri_end = p;
  24. r->http_minor = 9;
  25. goto done;

最终导致nginx认为此次请求的后缀名为php,通过配置,会传给fastcgi进行处理,而fastcgi在查找文件的时候被\0截断,最终取到a.jpg(非编码空格)文件(注:Linux下php-fpm默认限制的后缀名为php,如未取消限制,访问将出现access denied。测试想要查看执行结果,需修改php-fpm.conf中的security.limit_extensions为空,即允许任意后缀名文件作为php解析。)

Windows

首先,我们了解一下windows读取文件时的特点,即文件系统api创建文件名或查找文件名时,默认会去掉文件名后的空格,再执行操作,参见示例代码,目录下放置a.txt不带空格:

  1. #!cpp
  2. #include "stdafx.h"
  3. #include<windows.h>
  4. int _tmain(int argc, _TCHAR* argv[])
  5. {
  6. HANDLE hFile =CreateFile(L"a.txt ",GENERIC_WRITE|GENERIC_READ, 0, //注意a.txt后有一个空格
  7. NULL,
  8. OPEN_EXISTING, // 打开存在的文件
  9. FILE_ATTRIBUTE_NORMAL,
  10. NULL);
  11. if (hFile ==INVALID_HANDLE_VALUE)
  12. {
  13. printf("openfailed!");
  14. }
  15. else
  16. {
  17. printf("fileopened");
  18. }
  19. CloseHandle(hFile);
  20. return 0;
  21. }

通过此代码可知道,即使我们传入参数是”a.txt ”带空格,最后访问到却确是”a.txt”不带空格
此时的攻击过程为:

  1. 上传任意文件(不需要带空格文件),
  2. http://127.0.0.1/a.jpg(非编码空格)\0.php

    参考

    http://sec.baidu.com/index.php?research/detail/id/19