2018.11.18湖湘杯wp

1、签到

关注公众号,略。

2、HighwayHash64

根据题目名称以及里面的口算hash,结合着题目反汇编了解到这是一个hash算法,搜索得到哈希的源代码。

拖进ida里面,分析程序逻辑发现程序首先对输入的flag长度进行了一次hash验证需要等于一个16位hash,之后又对花括号当中的数字进行了hash验证,不成功就return 1。既然是一个hash算法,应该只有爆破了吧。先把长度给爆出来吧。想到最后要对内容爆破,长度应该不会很长,所以打算自己手试。发现里面都是exit(1),但是我们目前的目标是过掉第一个check,所以更改第二个exit的retcode为2,利用010editor把对应偏移的01改成02保存。

这样就可以bat的%ERRORLEVEL%对程序的retcode进行监测来判断是不是过掉了第一个check:

set ws=wscript.createobject("wscript.shell")

msgbox ""

dim a
dim i
a = 1
do while(a<=280)
	msgbox a
	i=1
	do while(i<=a)
		ws.sendkeys "1"
		i=i+1
	loop
	a=a+1
	ws.sendkeys "{ENTER}"
loop
@echo off
:next
reverse_2.exe
if %ERRORLEVEL% EQU 2 echo !!!!!number2!!!!! & goto next
if %ERRORLEVEL% EQU 1 echo again & goto next
if %ERRORLEVEL% EQU 0 echo ############   & goto next

 

 

两个程序配合使用可以试出来长度是19。这样掐头去尾中间的数字只有10位了。现在主要问题就是爆出来这10^10种可能。刚开始想了好多不去分析函数的方法,比如reverse_2.exe<1.txt这样传内容(每次参数都要写到文件再从文件读,实际效率会非常低,跑完硬盘会炸,放弃。);patch程序里面的内容来实现自动爆破(感觉应该是可以的不过并不靠谱,放弃)。只能选择看源码:

对照下载的hash函数源代码可以清晰的分析出来函数的过程,如图中红字所示。发现这个程序更改了HighwayHashRest里面的数据。所以也不用管什么key的,直接填上最后要返回的内容:

void HighwayHashReset(const uint64_t key[4], HighwayHashState* state) {
  uint64_t *a2 = (uint64_t*)state;
  a2[8] = 0x1BE6D5D5FE4CCE2F;
  a2[9] = 0x24093822299F31D0;
  a2[10] = 0x33198A2E03707344;
  a2[11] = 0x443F6A8885A308D3;
  a2[12] = 0x5BD39E10CB0EF593;
  a2[13] = 0x60ACF169B5F18A8C;
  a2[14] = 0x7E5466CF34E90C6C;
  a2[15] = 0x852821E638D01377;
  a2[0] = 0xCF0C0C1ED5EDF3E;
  a2[1] = a2[9] ^ 0x3F3E3D3C3B3A1918;
  a2[2] = a2[10] ^ 0x1226252423222121;
  a2[3] = a2[11] ^ 0x2F2E2D2C2B2A2928;
  a2[4] = a2[12] ^ 0x1312111117161514;
  a2[5] = a2[13] ^ 0x3B3A19183F3E3D3C;
  a2[6] = a2[14] ^ 0x2322212112262524;
  a2[7] = a2[15] ^ 0x2B2A29282F2E2D2C;
  /*state->mul0[0] = 0xdbe6d5d5fe4cce2full;
  state->mul0[1] = 0xa4093822299f31d0ull;
  state->mul0[2] = 0x13198a2e03707344ull;
  state->mul0[3] = 0x243f6a8885a308d3ull;
  state->mul1[0] = 0x3bd39e10cb0ef593ull;
  state->mul1[1] = 0xc0acf169b5f18a8cull;
  state->mul1[2] = 0xbe5466cf34e90c6cull;
  state->mul1[3] = 0x452821e638d01377ull;
  state->v0[0] = state->mul0[0] ^ key[0];
  state->v0[1] = state->mul0[1] ^ key[1];
  state->v0[2] = state->mul0[2] ^ key[2];
  state->v0[3] = state->mul0[3] ^ key[3];
  state->v1[0] = state->mul1[0] ^ ((key[0] >> 32) | (key[0] << 32));
  state->v1[1] = state->mul1[1] ^ ((key[1] >> 32) | (key[1] << 32));
  state->v1[2] = state->mul1[2] ^ ((key[2] >> 32) | (key[2] << 32));
  state->v1[3] = state->mul1[3] ^ ((key[3] >> 32) | (key[3] << 32));*/
}

这个小结构体直接当成4*4的uint64_t数组就行了。所以a[0]-a[15]完美对应。

为了检测上面替换的准确性,先按照原题把uint8_int数据19,长度4校验:

uint8_t data[11] = {19};

printf("%llx",HighwayHash64(data, 4, key));

发现输出结果吻合,ok可以开始爆破了!

#include "highwayhash.h"

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>


int main() {
  const uint64_t key[4]={0};
  uint8_t data[11]={0,0,0,0,0,0,0,0,0,0};
  for(uint8_t v1='9';v1>='0';v1--){
	for(uint8_t v2='9';v2>='0';v2--){
		for(uint8_t v3='9';v3>='0';v3--){
			for(uint8_t v4='9';v4>='0';v4--){
				for(uint8_t v5='9';v5>='0';v5--){
					for(uint8_t v6='9';v6>='0';v6--){
						for(uint8_t v7='9';v7>='0';v7--){
							for(uint8_t v8='9';v8>='0';v8--){
								for(uint8_t v9='9';v9>='0';v9--){
									for(uint8_t v10='9';v10>='0';v10--){
										data[0]=v1;
										data[1]=v2;
										data[2]=v3;
										data[3]=v4;
										data[4]=v5;
										data[5]=v6;
										data[6]=v7;
										data[7]=v8;
										data[8]=v9;
										data[9]=v10;
										if(HighwayHash64(data, 10, key) == 0xC886BDF39CB4ED72){
											puts((char *)data);
										}
										
  }    }    }    }    }    }    }    }    }  }
	
  //printf("%llx",HighwayHash64(data, 10, key));
  return 0;
}

 

 

感觉倒着跑应该更快,事实证明是非常正确的。开O3优化

得到结果9352641078,加上hxb外壳即为flag。

hxb2018{9352641078}

 

3、Replace

拖进ida发现是个UPX加壳,使用DIE查看验证的确是。ESP定律就可以脱壳。但是遇到了一点阻碍:

  

如果这样就会出现这个错误,原因是起始地址搞错了。

点击e,

改了之后拖出来发现不能运行,拖进ida不正常,就比较难受。只能用UPX脱壳机(UPXEasyGUI)试试了:

成功脱壳,然而脱出来之后不能运行,不过拖进ida就一片光明了,开始分析程序逻辑。

就一个check,里面是个逐位校验的过程,所以逐位爆破即可。

这里我犯了个错:= =|

这里字符串应该取ascii码的hex值,可我直接把这个两位两位拆开了,浪费了好多时间,不然就秒了……

也可以直接从od动调内存里抓

直接复制反汇编的c代码跑c++:(注意一定是unsigned char)

#include<iostream>

using namespace std;

int main(){
	unsigned char byte_402150[] = {0x32,0x61,0x34,0x39,0x66,0x36,0x39,0x63,0x33,0x38,0x33,0x39,0x35,0x63,0x64,0x65,0x39,0x36,0x64,0x36,0x64,0x65,0x39,0x36,0x64,0x36,0x66,0x34,0x65,0x30,0x32,0x35,0x34,0x38,0x34,0x39,0x35,0x34,0x64,0x36,0x31,0x39,0x35,0x34,0x34,0x38,0x64,0x65,0x66,0x36,0x65,0x32,0x64,0x61,0x64,0x36,0x37,0x37,0x38,0x36,0x65,0x32,0x31,0x64,0x35,0x61,0x64,0x61,0x65,0x36};
unsigned char byte_4021A0[] =  {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,0x48};

  char *v2; // ebx
  int v4; // edx
  char v5; // al
  int v6; // esi
  int v7; // edi
  char v8; // al
  int v9; // eax
  char v10; // cl
  int v11; // eax
  int v12; // ecx
  
  v4 = 0;
  while ( 1 )
  {
  	for(int i=32;i<127;i++){
    v5 = i;
    v6 = (v5 >> 4) % 16;
    v7 = (16 * v5 >> 4) % 16;
    v8 = byte_402150[2 * v4];
    if ( v8 < 48 || v8 > 57 )
      v9 = v8 - 87;
    else
      v9 = v8 - 48;
    v10 = byte_402150[2 * v4 + 1];
    v11 = 16 * v9;
    if ( v10 < '0' || v10 > '9' )
      v12 = v10 - 87;
    else
      v12 = v10 - 48;
    if (byte_4021A0[16 * v6 + v7] != ((v11 + v12) ^ 0x19) )
      continue;
    cout << v4 << ' ' << i << endl; 
    
  	}
  	if ( ++v4 >= 35 )
      return 1;
  }
  return -1;
}
 

 

 

得到ascii转码得到flag:

flag{Th1s_1s_Simple_Rep1ac3_Enc0d3}

 

4、Flow

下下来是一个wifi流量包,先扔进kali跑字典:

aircrack-ng '/root/桌面/ctf.pcap' -w '/root/桌面/wordlist1.txt'

password1是密码。

首选项里面添加key,ok之后得到解密的流量。过滤http请求:

直接看到了flag,还以为是fake的,结果提交成功~

flag{H4lf_1s_3n0ugh}

5、Disk

下载下来是一个vmdk文件,使用DiskGenius等磁盘软件尝试打开均提示文件损坏。发现文件是有-flat标记,说明是虚拟机运行时宿主机直接断电导致的虚拟磁盘损坏。

使用testdisk对虚拟磁盘文件进行修复:

testdisk_win.exe ctf-flat.vmdk

搜索到一个分区,按p读取文件

提取txt说这里没有flag,所以就是在这ads文件里面了。

发现有ads结尾的文件,查阅资料得:

NTFS交换数据流(Alternate Data Streams,简称ADS)是NTFS磁盘格式的一个特性。在NTFS文件系统下,每个文件都可以存在多个数据流,意思是除了主文件流之外还可以有许多非主文件流寄宿在主文件流中,这些利用NTFS数据流寄宿并隐藏在系统中的非主文件流我们称之为ADS流文件。

这种ADS流文件常存在于NTFS磁盘当中作为隐写形式。所以导出这四个ads文件。

合并

010editor看一下:

感觉这其实应该是二进制,所以把这四个文件合并,python读出来hex流,然后放到decode网站上进行解码:

copy flag0.txt_ads+flag1.txt_ads+flag2.txt_ads+flag3.txt_ads flagg.txt_ads
f = open('flagg.txt_ads','rb')

con = f.read()

print con.encode('hex')#python2

二进制转ascii得到flag:http://tool.ph0en1x.com/hashtool/tools.html#conv/

flag{4DS_1n_D1sk}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值