• [HFCTF2020]BabyUpload session解析引擎


    1. <?php
    2. error_reporting(0);
    3. session_save_path("/var/babyctf/");//session存放位置
    4. session_start();
    5. require_once "/flag";
    6. highlight_file(__FILE__);
    7. if($_SESSION['username'] ==='admin')
    8. {
    9. $filename='/var/babyctf/success.txt';
    10. if(file_exists($filename)){
    11. safe_delete($filename);
    12. die($flag);
    13. }
    14. }
    15. else{
    16. $_SESSION['username'] ='guest';
    17. }
    18. $direction = filter_input(INPUT_POST, 'direction');//filter_input() 函数从脚本外部获取输入(post) 变量direction
    19. $attr = filter_input(INPUT_POST, 'attr');//post 变量attr
    20. $dir_path = "/var/babyctf/".$attr;//拼接路径$dir_path =/var/babyctf/$attr
    21. if($attr==="private"){
    22. $dir_path .= "/".$_SESSION['username'];//$dir_path =/var/babyctf/private/$_SESSION['username']
    23. }
    24. if($direction === "upload"){
    25. try{
    26. if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){//is_uploaded_file() 函数检查指定的文件是否是通过 HTTP POST 上传
    27. throw new RuntimeException('invalid upload');
    28. }//参数up_file
    29. $file_path = $dir_path."/".$_FILES['up_file']['name'];//$file_path =/var/babyctf/$attr/$_FILES['up_file']['name']
    30. $file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
    31. //$file_path =/var/babyctf/$attr/$_FILES['up_file']['name']_文件名的sha256
    32. if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){// ../ ..\\限制目录穿越
    33. throw new RuntimeException('invalid file path');
    34. }
    35. @mkdir($dir_path, 0700, TRUE);//创建目录/var/babyctf/$attr/
    36. if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
    37. $upload_result = "uploaded";//文件上传到$file_path下 P31
    38. }else{
    39. throw new RuntimeException('error while saving');
    40. }
    41. } catch (RuntimeException $e) {
    42. $upload_result = $e->getMessage();
    43. }
    44. } elseif ($direction === "download") {
    45. try{
    46. $filename = basename(filter_input(INPUT_POST, 'filename'));//读取文件名 post传入filename
    47. $file_path = $dir_path."/".$filename;//$dir_path =/var/babyctf/$attr/$filename
    48. if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){//限制目录穿越
    49. throw new RuntimeException('invalid file path');
    50. }
    51. if(!file_exists($file_path)) {//文件是否存在
    52. throw new RuntimeException('file not exist');
    53. }
    54. header('Content-Type: application/force-download');
    55. header('Content-Length: '.filesize($file_path));
    56. header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
    57. if(readfile($file_path)){//文件是否存在
    58. $download_result = "downloaded";
    59. }else{
    60. throw new RuntimeException('error while saving');
    61. }
    62. } catch (RuntimeException $e) {
    63. $download_result = $e->getMessage();
    64. }
    65. exit;
    66. }
    67. ?>

    对于代码的解析如上,获取flag的途径:

    1、session中的username参数要为admin

    2、在 /var/babyctf/success.txt存在

    在代码审查中可以得到的信息点:

    1. 一共有四个参数 direction attr filename up_file
    2. 1
    3. direction为upload的时候为上传文件状态
    4. attr传入的值拼接为路径$dir_path = "/var/babyctf/".$attr
    5. 并且中间有一个mkdir函数创建目录 @mkdir($dir_path, 0700, TRUE) 代表我们当前所在的目录由$attr参数决定
    6. 文件名会被修改为
    7. $file_path =/var/babyctf/$attr/$_FILES['up_file']['name']_文件名的sha256
    8. 也就是文件名后面加一个 _ 和文件的msha256值
    9. up_file是我们上传文件的参数,可以利用postman工具上传我们所需的文件
    10. 2
    11. direction为download的时候为读取文件状态
    12. filename参数控制文件名,可以根据前面上传后的加密来破解文件名
    13. 路径如$dir_path =/var/babyctf/$attr/$filename
    14. 如果文件存在,会输出文件内容

    在php中,session文件默认的存储文件名是sess_PHPSESSID,于是我们可以利用donwnload先看一下原先的session文件里面有什么,经过分析可以知道$attr参数我们可以暂时设置为空,因为目前感觉跟我们的操作没多大关系

    attr=&direction=download&filename=sess_609f6cd6a2922894f3843927cb598f65

    可以发现有一个不可见字符,不同的php引擎,session的存储方式不一样

    1. php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
    2. php:存储方式是,键名+竖线+经过serialize()函数序列处理的值
    3. php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值

     可以知道我们这题的php引擎为php_binary,于是取伪造session文件

    1. <?php
    2. ini_set('session.serialize_handler','php_binary');
    3. session_save_path('D:\Phpstorm\file\[HFCTF2020]BabyUpload');
    4. session_start();
    5. $_SESSION['username'] = 'admin';

    为了方便文件名的计算,这边建议把文件名改为sess方便我们后面的计算

     格式没有错误(这里不要以为是usernames,只是凑在一起了,键名username+serialize的值,s是字符的意思)

    然后利用postman工具利用参数up_file参数传入sess文件(我们就不用在burp上构造文件信息了)

    记得将方法改为POST,我开始一直方法没改,GET传入一直出不来…………

     上传是否成功我们可以利用之前的查看办法去看,但是这里我们要先知道该session文件sha256后的值

    1. echo hash_file('sha256','D:\Phpstorm\file\[HFCTF2020]BabyUpload\sess');
    2. //432b8b09e30c4a75986b719d1312b63a69f1b833ab602c9ad5f0299d1d76a5a4

    于是我们改一下filename即可

     现在session文件成功伪造,目的就是如何上传success.txt文件,因为前面的分析可以知道文件名最后还要加_以及sha256加密,于是这个方法行不通

    1. if($_SESSION['username'] ==='admin')
    2. {
    3. $filename='/var/babyctf/success.txt';
    4. if(file_exists($filename)){
    5. safe_delete($filename);
    6. die($flag);
    7. }
    8. }

    查一下file_exists有什么漏洞

    目录!我们可以利用att参数创建一个success.txt文件夹,然后将sess传入success目录下,因为后面有个mkdir获取的还是我们的session文件的值,但是这个文件判断是在babyctf目录下!

    bingo!!!赶紧试试

    去浏览器将自己的phpsession改为我们之前那个sha256的文件名,以此欺骗我们是admin

    ,即可获取flag 

     

  • 相关阅读:
    带你掌握 Makefile 分析
    链家网房源价格信息的爬虫分析工具
    Flink Watermark详解
    网站部署与上线(2)远程连接云服务器或虚拟机
    【3D目标检测】KITTI数据集介绍
    介绍如何在Go中使用字符串
    postgresql模糊查询(like和~)引用变量
    Go语言并发控制
    AMAZINGIC晶焱科技推出低操作电压ESD保护元件
    HTML期末学生大作业:基于html+css+javascript+jquery企业餐厅11页 企业网站制作
  • 原文地址:https://blog.csdn.net/qq_54929891/article/details/125584569