奥运会网球比分规则|雪缘网网球比分直播|
欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

从PHP底层看open_basedir bypass

来源:本站整理 作者:佚名 时间:2019-04-15 TAG: 我要投稿


有国外的大佬近日公开了一个php open_basedir bypass的poc,正好最近在看php底层,于是打算分析一下。
poc测试
首先测试一下:

我们用如上源码进行测试,首先设置open_basedir目录为/tmp目录,再尝试用ini_set设置open_basedir则无效果,我们对根目录进行列目录,发现无效,返回bool(false)。
我们再尝试一下该国外大佬的poc:

发现可以成功列举根目录,bypass open_basedir。
那么为什么一系列操作后,就可以重设open_basedir了呢?我们一步一步从头探索。
ini_set覆盖问题探索
为什么连续使用ini_set不会对open_basedir进行覆盖呢?我们以如下代码为例:
运行后结果如下:
string(0) ""
string(4) "/tmp"
string(4) "/tmp"
string(4) "/tmp"
默认的open_basedir值本来是空,第一次设置成/tmp后,以为设置将不会覆盖。
我们来探索一下原因。首?#26085;?#21040;php函数对应的底层函数:
ini_get : PHP_FUNCTION(ini_get)
ini_set : PHP_FUNCTION(ini_set)
这里我们主要看的是ini_set的流程,ini_get作为信息输出函数,我们不太关心。
我们先对ini_set下?#31995;悖?#28982;后再run程序:
b /php7.0-src/ext/standard/basic_functions.c 5350
r c.php
程序跑起来后,首先是3个初始值:
zend_string *varname;
zend_string *new_value;
char *old_value;
然后进入词法分析,得到3个变量值:
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &varname, &new_value) == FAILURE) {
return;
}
我们可以看到
pwndbg> p *varname
$45 = {
  gc = {
    refcount = 0,
    u = {
      v = {
        type = 6 '\006',
        flags = 2 '\002',
        gc_info = 0
      },
      type_info = 518
    }
  },
  h = 15582417252668088432,
  len = 12,
  val = "o"
}
这是zend_string的结构体,也是php7的新增结构:
struct _zend_string {
    zend_refcounted_h gc; /*gc信息*/
    zend_ulong        h;  /* hash value */
    size_t            len; /*字符串长度*/
    char              val[1]; /*字符串起始地址*/
};
我们可以看到varname.val为:
pwndbg> p &varname.val
$46 = (char (*)[1]) 0x7ffff7064978
pwndbg> x/s $46
0x7ffff7064978:"open_basedir"
然后new_value.val为:
pwndbg> p &new_value.val
$48 = (char (*)[1]) 0x7ffff7058ad8
pwndbg> x/s $48
0x7ffff7058ad8:"/tmp"
即我们最开始传入的两个?#38382;?br/> 然后程序拿到原来的open_basedir的value:


然后会进入php_ini_check_path:

由于第一次没有设置过open_basedir,所以直接跳出判断,进入下一步:
if (zend_alter_ini_entry_ex(varname, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
我们跟进FAILURE,找到定义:
typedef enum {
  SUCCESS =  0,
  FAILURE = -1,/* this MUST stay a negative number, or it may affect functions! */
} ZEND_RESULT_CODE;
当zend_alter_ini_entry_ex的返回值不为-1时,即代表更新成功,否则则会进入if,返回false。
而经过比对发现:第一次设置open_basedir和第二次设置时候,正是这里的返回值不一样,第一次设置时,这里为SUCCESS,即0,而第二次设置为FAILURE,即-1,我们跟入zend_alter_ini_entry_ex进行比对:
b /php7.0-src/Zend/zend_ini.c:330
发现两次不同的点在于如下判断:
if (!ini_entry->on_modify
|| ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS)
第一次时:
ini_entry->on_modify = 0x5d046e
ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) = 0

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.nddver.tw)登载此文出于传递更多信息之目的,并不代表本站赞同其观点?#25237;?#20854;真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱[email protected],我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载
        奥运会网球比分规则