php扩展流程

先 模块初始化阶段(MINIT)注册常量或者类等初始化操作
然后 模块激活阶段(RINIT)该过程发生在请求阶段, 例如通过url请求某个页面,则在每次请求之前都会进行模块激活(RINIT请求开始)。 例如PHP注册了一些扩展模块

taint

taint主要由三部分构成,污点标记、污点传播、污点沉降

污点方法

//定义mark规则
#define TAINT_MARK(str)        (GC_FLAGS((str)) |= IS_STR_TAINT_POSSIBLE)
#define TAINT_POSSIBLE(str) (GC_FLAGS((str)) & IS_STR_TAINT_POSSIBLE)
#define TAINT_CLEAN(str)      (GC_FLAGS((str)) &= ~IS_STR_TAINT_POSSIBLE)

我们根据污点函数可以看到 不同版本添加污点的方式不同
是在 php_taint.h 里面了

# if PHP_VERSION_ID >=70300
#  define EX_CONSTANT(op) RT_CONSTANT(EX(opline), op)
#  undef IS_STR_TAINT_POSSIBLE
#  define IS_STR_TAINT_POSSIBLE (1<<5) /* GC_PROTECTED */
#  define TAINT_MARK(str)     GC_ADD_FLAGS(str, IS_STR_TAINT_POSSIBLE)
#  define TAINT_POSSIBLE(str) (GC_FLAGS((str)) & IS_STR_TAINT_POSSIBLE)
#  define TAINT_CLEAN(str)    GC_DEL_FLAGS(str, IS_STR_TAINT_POSSIBLE)
# else
#  define TAINT_MARK(str)     (GC_FLAGS((str)) |= IS_STR_TAINT_POSSIBLE)
#  define TAINT_POSSIBLE(str) (GC_FLAGS((str)) & IS_STR_TAINT_POSSIBLE)
#  define TAINT_CLEAN(str)    (GC_FLAGS((str)) &= ~IS_STR_TAINT_POSSIBLE)
# endif

然后在c文件中可以看到对这三个函数的调用

PHP_RINIT_FUNCTION

对输入源进行污点标记
模块被调用的函数 PHP_RINIT_FUNCTION
可以看到数据输入层面只有这三点,对于webshell的检查肯定是不够的 比如 从请求头中 从环境变量中等等

    // 污染post数据if (Z_TYPE(PG(http_globals)[TRACK_VARS_POST]) == IS_ARRAY) {php_taint_mark_strings(Z_ARRVAL(PG(http_globals)[TRACK_VARS_POST]));}// 污染get数据if (Z_TYPE(PG(http_globals)[TRACK_VARS_GET]) == IS_ARRAY) {php_taint_mark_strings(Z_ARRVAL(PG(http_globals)[TRACK_VARS_GET]));}// 污染cookie数据if (Z_TYPE(PG(http_globals)[TRACK_VARS_COOKIE]) == IS_ARRAY) {php_taint_mark_strings(Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]));}

使用这个函数污染 php_taint_mark_strings

例如这种是全局变量 TRACK_VARS_COOKIE 还有啥呢

TRACK_VARS_POST
TRACK_VARS_GET
TRACK_VARS_FILES
TRACK_VARS_COOKIE
TRACK_VARS_ENV
TRACK_VARS_SERVER
TRACK_VARS_REQUEST

这些是数据输入点 外部传入的话都会打污点标记

标记所有的GET、COOKIE、POST、SERVER这些array中的每个key->value初始标记为污点

static void php_taint_mark_strings(zend_array *symbol_table) /* {{{ */ {zval *val;ZEND_HASH_FOREACH_VAL(symbol_table, val) {ZVAL_DEREF(val);if (Z_TYPE_P(val) == IS_ARRAY) {php_taint_mark_strings(Z_ARRVAL_P(val));} else if (IS_STRING == Z_TYPE_P(val) && Z_STRLEN_P(val)) {TAINT_MARK(Z_STR_P(val));}} ZEND_HASH_FOREACH_END();
} /* }}} */

PHP_MINIT_FUNCTION 数据传递

 php_taint_register_handlers(); // 进行关键执行函数hook  通过opcodephp_taint_override_functions(); // 对数据传递的函数进行hook 通过劫持handler

在数据传递的过程中对传递的数据也认为是污染的
有哪些函数是危险函数并且认为会污染数据的
php_taint_override_functions

static void php_taint_override_functions() /* {{{ */ {const char *f_join         = "join";const char *f_trim         = "trim";const char *f_split        = "split";const char *f_rtrim        = "rtrim";const char *f_ltrim        = "ltrim";const char *f_strval       = "strval";const char *f_strstr       = "strstr";const char *f_substr       = "substr";const char *f_sprintf      = "sprintf";const char *f_explode      = "explode";const char *f_implode      = "implode";const char *f_str_pad      = "str_pad";const char *f_vsprintf     = "vsprintf";const char *f_str_replace  = "str_replace";const char *f_str_ireplace = "str_ireplace";const char *f_strtolower   = "strtolower";const char *f_strtoupper   = "strtoupper";const char *f_dirname      = "dirname";const char *f_basename     = "basename";const char *f_pathinfo     = "pathinfo";php_taint_override_func(f_strval, PHP_FN(taint_strval), &TAINT_O_FUNC(strval));php_taint_override_func(f_sprintf, PHP_FN(taint_sprintf), &TAINT_O_FUNC(sprintf));php_taint_override_func(f_vsprintf, PHP_FN(taint_vsprintf), &TAINT_O_FUNC(vsprintf));php_taint_override_func(f_explode, PHP_FN(taint_explode), &TAINT_O_FUNC(explode));php_taint_override_func(f_split, PHP_FN(taint_explode), NULL);php_taint_override_func(f_implode, PHP_FN(taint_implode), &TAINT_O_FUNC(implode));php_taint_override_func(f_join, PHP_FN(taint_implode), NULL);php_taint_override_func(f_trim, PHP_FN(taint_trim), &TAINT_O_FUNC(trim));php_taint_override_func(f_rtrim, PHP_FN(taint_rtrim), &TAINT_O_FUNC(rtrim));php_taint_override_func(f_ltrim, PHP_FN(taint_ltrim), &TAINT_O_FUNC(ltrim));php_taint_override_func(f_str_replace, PHP_FN(taint_str_replace), &TAINT_O_FUNC(str_replace));php_taint_override_func(f_str_ireplace, PHP_FN(taint_str_ireplace), &TAINT_O_FUNC(str_ireplace));php_taint_override_func(f_str_pad, PHP_FN(taint_str_pad), &TAINT_O_FUNC(str_pad));php_taint_override_func(f_strstr, PHP_FN(taint_strstr), &TAINT_O_FUNC(strstr));php_taint_override_func(f_strtolower, PHP_FN(taint_strtolower), &TAINT_O_FUNC(strtolower));php_taint_override_func(f_strtoupper, PHP_FN(taint_strtoupper), &TAINT_O_FUNC(strtoupper));php_taint_override_func(f_substr, PHP_FN(taint_substr), &TAINT_O_FUNC(substr));php_taint_override_func(f_dirname, PHP_FN(taint_dirname), &TAINT_O_FUNC(dirname));php_taint_override_func(f_basename, PHP_FN(taint_basename), &TAINT_O_FUNC(basename));php_taint_override_func(f_pathinfo, PHP_FN(taint_pathinfo), &TAINT_O_FUNC(pathinfo));} /* }}} */

怎么hook的呢

劫持hadnler

保留原来函数的handler后替换handler为我们hook的函数

比如 taint_strval 输出字符串值

PHP_FUNCTION(taint_strval) {zval *num;int tainted = 0;if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &num) == FAILURE) {return;}// 判断参数是否有污点 有污点 tainted 设置为1if (Z_TYPE_P(num) == IS_STRING && TAINT_POSSIBLE(Z_STR_P(num))) {tainted = 1;}// 调用真实的函数TAINT_O_FUNC(strval)(INTERNAL_FUNCTION_PARAM_PASSTHRU);// 如果之前是污点的 有返回值且返回值不等于参数 并且 返回值不是空 就污染返回值if (tainted && IS_STRING == Z_TYPE_P(return_value) && Z_STR_P(return_value) != Z_STR_P(num) && Z_STRLEN_P(return_value)) {TAINT_MARK(Z_STR_P(return_value));}
}

其他函数的写法类似

我们可以看到这个是不全的,例如对base64编码的就没有进行hook

PHP_MINIT_FUNCTION 数据执行

对传入的数据最后的执行函数的opcode进行hook

在函数 PHP_MINIT_FUNCTION (注册常量或者类等初始化操作) 中

 php_taint_register_handlers();php_taint_override_functions();

php_taint_register_handlers 中opcode进行hook

static void php_taint_register_handlers() /* {{{ */ {int idx;for (idx = 0; idx < sizeof(override_opcode_handlers)/sizeof(taint_custom_handler); idx++) {origin_opcode_handler[idx] = (void*)zend_get_user_opcode_handler(override_opcode_handlers[idx].opcode);}for (idx = 0; idx < sizeof(override_opcode_handlers)/sizeof(taint_custom_handler); idx++) {zend_set_user_opcode_handler(override_opcode_handlers[idx].opcode, (user_opcode_handler_t)override_opcode_handlers[idx].handler);}return;
} /* }}} */

所有hook的数据执行函数

static const taint_custom_handler override_opcode_handlers[] = {{ ZEND_ECHO, php_taint_echo_handler },{ ZEND_EXIT, php_taint_exit_handler },{ ZEND_INIT_USER_CALL, php_taint_init_dynamic_fcall_handler },{ ZEND_INIT_DYNAMIC_CALL, php_taint_init_dynamic_fcall_handler },{ ZEND_INCLUDE_OR_EVAL, php_taint_include_or_eval_handler },{ ZEND_CONCAT, php_taint_concat_handler },{ ZEND_FAST_CONCAT, php_taint_concat_handler },
#if PHP_VERSION_ID < 70400{ ZEND_ASSIGN_CONCAT, php_taint_assign_concat_handler },
#else{ ZEND_ASSIGN_OP, php_taint_assign_op_handler },{ ZEND_ASSIGN_DIM_OP, php_taint_assign_dim_op_handler },{ ZEND_ASSIGN_OBJ_OP, php_taint_assign_obj_op_handler },
#endif{ ZEND_ROPE_END, php_taint_rope_handler },{ ZEND_DO_FCALL, php_taint_fcall_handler },{ ZEND_DO_ICALL, php_taint_fcall_handler },{ ZEND_DO_FCALL_BY_NAME, php_taint_fcall_handler }
};

把所有的函数进行hook 使用 zend_set_user_opcode_handler

最后是判断逻辑
是根据opcode进行hook关键的执行函数

比如说 echo php_taint_echo_handler
如果给关键执行函数传入的参数是污点的 那么就输出 危险

static int php_taint_echo_handler(zend_execute_data *execute_data) /* {{{ */ {const zend_op *opline = execute_data->opline;taint_free_op free_op1;zval *op1;op1 = php_taint_get_zval_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_R, 0);if (op1 && IS_STRING == Z_TYPE_P(op1) && TAINT_POSSIBLE(Z_STR_P(op1))) {if (opline->extended_value) {php_taint_error("print", "Attempt to print a string that might be tainted");} else {php_taint_error("echo", "Attempt to echo a string that might be tainted");}}CALL_ORIGIN_HANDLER();return ZEND_USER_OPCODE_DISPATCH;
} /* }}} */

检测webshell

我们先简单写一个一句话并且在taint中加一些debug信息

可以看到先进入minit 先使用opcode hook关键的执行函数 然后hook 数据传递的函数
再之后进入rinit 对数据输入进行污点标记 最后执行关键函数的数据值是污染的那么就认为有问题

如何去除污点

  1. 对于 数据经过排除hook的危险函数的函数则返回值产生新的变量的则相当于去除了污点
  2. 对于特定的可控的可以我们自定义去手动去除污点 TAINT_CLEAN

一些问题

如果对webshell加上@ 至少默认情况下 taint不会输出关键信息 绕过 对于没有hook的传递函数和数据输入点都会产生绕过

之前一直以为是会有一个污染链路的情况,没想到只是把所有外部输入和部分危险函数的数据标记为污点后最后判断关键执行点。然后这个链路可以我们自己去加

参考

taint 污点追踪分析的一些文章
https://www.cnblogs.com/ermei/p/9778021.html
https://www.dazhuanlan.com/2020/04/01/5e84699490d0c/
https://www.laruence.com/tag/taint
https://xz.aliyun.com/t/4268
https://www.jianshu.com/p/c6dea66c54f3

扩展概念
https://www.jianshu.com/p/98eec8b08a8e

PHP 扩展添加方法
https://xz.aliyun.com/t/4214

https://xz.aliyun.com/t/7316
https://www.anquanke.com/post/id/98938

taint扩展的策略和敏感函数黑名单
https://www.cnblogs.com/linzhenjie/p/5485474.html

php语法解析
python版的php语法解析
https://github.com/g-i-o-/pyphp
https://github.com/Alexey-T/pyPhpTree
go版本的php语法解析
https://github.com/z7zmey/php-parser
https://github.com/stephens2424/php
python版通用的高级语法解析
https://github.com/autosoft-dev/tree-hugger

简单了解taint 污点追踪相关推荐

  1. 【项目实战23】k8s(9)—k8s调度(节点亲和性,Taint污点,pod容忍性)

    k8s调度 一.k8s调度背景介绍 二.nodeName方式调度 三.nodeSelector方式调度 (1).使用方式 (2).节点亲和性 四.Taints污点 (1).介绍 (2).使用 五.to ...

  2. Kubernetes调度器-Pod分配给节点(Taint污点和Toleration容忍)

    Taint和Toleration 节点亲和性是pod的一种属性(偏好或硬性要求),它使pod被吸引到一类特定的节点.Taint则相反,它使节点能够排斥一类特定的pod. Taint和toleratio ...

  3. 动态污点分析——用于商用软件漏洞的自动检测分析和签名生成【论文概述】

    技术概览:动态污点分析技术是旨在通过对运行时间内重写二进制代码的表现进行分析,从而检测和过滤攻击行为并以此提高系统安全性.通过试验表明,动态污点分析技术能够检测出绝大多数的软件漏洞,如果使用混合检测器 ...

  4. Kubernetes Pod调度进阶:Taints(污点)和Tolerations(容忍)

    [注意]最后更新于 2 years ago,文中内容可能已过时,请谨慎使用. 污点(Taint)和容忍(Toleration)是从Kubernetes 1.6开始提供的高级调度功能. 在Kuberne ...

  5. mppt多峰追踪MATLAB仿真,基于光伏功率等效面积法的多峰最大功率追踪控制方法...

    0引言在光伏系统实际应用中,由于天上移动的云朵.电池板累积的灰尘以及城镇中周围建筑物等的影响,光伏阵列在运行过程中总会受到不同程度的阴影遮挡,太阳能电池板的P-U曲线会受到影响出现多个峰值点.传统的M ...

  6. k8s核心技术-Pod(调度策略)_影响Pod调度(污点和污点容忍)---K8S_Google工作笔记0027

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 上面咱们说了亲和性,其实还有一个调度规则,就是反亲和性 ,根据反亲和性进行调度. 所谓的反亲和性就 ...

  7. keras-yolov3 + Kalman-Filter 进行人体多目标追踪(含代码)

    笔者最近在做新零售智慧门店的相关项目,主要涵盖人流量.人物活动区域轨迹等.那么本篇其实是笔者在实践过程中一个"失败"的案例,因为其应用复用在现实场景的时候效果非常差,所以只是当做练 ...

  8. html用bmob做留言,Bmob 之 简单使用

    1. pod pod 'BmobSDK' 与 pod "BmobSDK" 好像没什么区别 2. 导入 在AppDelegate中: #import [Bmob registerWi ...

  9. 【毕业设计】基于STM32及OpenMV的云台追踪装置

    目录 修改记录 1.摘 要 2.整体功能分析 3.硬件选型 3.1 OpenMV4 Cam H7 3.2 STM32F103ZET6 3.3 DS3120舵机 3.4 LED补光板 3.5 供电及稳压 ...

最新文章

  1. 网易有道的产品总监王焱:如何利用数据分析推动产品设计
  2. qt中关于坐标的说明
  3. jmeter参数值只读取了第一个_基础入门篇Jmeter(五)_函数
  4. leetcode刷题六z字形变换
  5. 四种方式话Equal
  6. selenium+unittest自动化测试(二)---浏览器控制及元素定位的方法
  7. oracle 数据库文件丢失
  8. steam怎么设置邮箱令牌_steam盗号?这样做50%能够避免损失!
  9. 多领域中文语音识别数据集 WenetSpeech 正式发布——有效下载教程
  10. 使用Typora+PicGo+Gitee+坚果云搭建免费高效的个人云笔记
  11. C#实现计算机远程操作
  12. 预备内容:---软件安装篇(1)
  13. android hook方法替换,Android Hook入门教程
  14. 第188天:extend拷贝创建对象的原理
  15. 全面剖析Redis Cluster原理和应用
  16. 2020年30岁IT人失业的一些感悟,希望对年轻人有帮助
  17. 机器学习与人工智能资源库
  18. 怎样用计算机做一个电子地图,电子地图如何制作简介-20210525123653.docx-原创力文档...
  19. Cat Party (Easy Edition)
  20. 张近东:企业的稳健发展是最大的社会责任

热门文章

  1. 什么是Web数据库应用程序?
  2. SQL中的DQL查询语言
  3. python中的sql(pymysql和sqlalchemy)
  4. java pmd 安装_PMD的安装及使用
  5. 【每天学习一点新知识】如何绕过CDN查真实ip
  6. pycharm this license has been suspended
  7. 编写程序实现披萨的制作
  8. UEFI使用rEFInd引导Win10+Deepin双系统
  9. 不是吧!都2021年了!软件测试工程师还要加班撸代码吗?
  10. 纯前端实现一键生成二维码,打开新页面展示二维码