The 3rd Shaanxi College Students Network Security Skills Competition in 2023--Reverse Questions for Undergraduate College Group


Three questions were made in this competition, Web&Assembly found tools and related articles, but the time is not enough hhh, the harvest is still a lot

1. Why is my upx -d broken?

1. View section information

It can be seen that the section area has been modified. It should have been changed from UPX to HOEY. After trying to modify it, it still cannot upx -d, so I chose to adjust
insert image description here

2. Dynamic debugging unpacking

Here it is recommended to use the special OD for Wuai Crack to open it. You can quickly find the key code of popad. Neither x32dbg nor ollydbg will work
. I saw popad, and there is a loop to adjust the stack below this, and there is a far jump of jmp 0x004012D0, so it is likely to be oep
Please add a picture description

After f7 followed up 4012D0, use OllyDump in the plug-in to unpack
Please add a picture description

Open and analyze the unpacked file, shift+f12 to view the string, you can see a string of suspicious strings in the first line, then follow up, and then press x to cross-reference the function to find the main function, you can see It is a maze, and it can be seen that the maze is 15 elements per row
insert image description here

3. Output maze map

#include <stdio.h>
#include <string.h>
int main() {
    
    
    char maze[] = "****************S000*0000000***0**000*****0***0*00*0*000*0***000**0*0*0*0***0***00*0*0*00**0***0**0*0**0**000*0*00*00*0****0*0******0#****0*00000000***000********0***0***00000*00***0*0*****0**0***0000000*00000****************";
    for (int i = 0; i < strlen(maze); i++)
    {
    
    
        if (i  % 15 == 0)
            printf("\n");
        printf("%c", maze[i]);

    }
    return 0;
}

Get the maze map:

***************
*S000*0000000**
*0**000*****0**
*0*00*0*000*0**
*000**0*0*0*0**
*0***00*0*0*00*
*0***0**0*0**0*
*000*0*00*00*0*
***0*0******0#*
***0*00000000**
*000********0**
*0***00000*00**
*0*0*****0**0**
*0000000*00000*
***************

4. Walk the maze

I wrote a maze-walking script before, and I can solve the path of this problem with a little modification. The
C language realizes automatic maze-walking and automatically outputs the maze path
. After modification:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <Windows.h>
// 枚举一些关键常量,可以根据迷宫的不同而修改
enum state
{
    
    
	start = 'S', end = '#', road = '0', wall = '*', visited = '1', successPath = '%', currentPosition = '@'
}State;
//路径操作符枚举
enum operate {
    
    
	up = 'U', right = 'R', down = 'D', left = 'L'
}Operate;
//保存路径
struct
{
    
    
	int len;
	unsigned char arr[1000];
}Path;
//输入路径
void inputPath(unsigned char op)
{
    
    
	Path.arr[Path.len] = op;
	Path.len++;
}
//输出路径
void printPath()
{
    
    
	printf("\nPath:");
	while (Path.len > 0)
	{
    
    
		Path.len--;
		putchar(Path.arr[Path.len]);
	}
	printf("\n");
}
//判断是否在迷宫范围内以及是否可以走这一步
bool isLegal(int x, int y, int row, int col, unsigned char* p)
{
    
    
	if (x >= 0 && y >= 0)
		if (x < row && y < col)
			return (p[x * col + y] == road || p[x * col + y] == end);
	return false;
}
//输入迷宫图
//支持以矩阵形式输入,也可以输入一整行,自动处理换行符,直到读取到整个迷宫图为止
void inputMaze(unsigned char* p, int row, int col)
{
    
    
	unsigned char ch;
	printf("请输入迷宫图:\n");
	for (int i = 0; i < row * col; i++)
	{
    
    
		if ((ch = getchar()) != '\n')
			p[i] = ch;
		else
			--i;
	}

}
//打印迷宫图
void printMaze(unsigned char* p, int row, int col) {
    
    
	printf("\n迷宫图如下:\n");
	for (int i = 0; i < row; i++)
	{
    
    
		for (int j = 0; j < col; j++)
			printf("%c", p[i * col + j]);
		printf("\n");
	}

}
//走迷宫
//递归查询,这里由于递归是倒序输出路径,所以需要一个倒序操作
bool walkMaze(int row, int col, unsigned char* p, int x, int y)
{
    
    
	int pos = x * col + y;	//当前位置
	if (p[pos] == end)		//到达终点
		return true;
	if (isLegal(x - 1, y, row, col, p))	//上
	{
    
    
		//printMaze(p,row,col); //如果需要可以逐步输出迷宫图
		p[pos] = visited;	//设置访问标识,防止无限递归
		if (walkMaze(row, col, p, x - 1, y))	//该路径可行,输出操作符
		{
    
    
			inputPath(up);
			p[pos] = successPath;			//用于显示该路径
			return true;
		}
	}
	if (isLegal(x, y + 1, row, col, p))	//右
	{
    
    
		//printMaze(p,row,col);
		p[pos] = visited;
		if (walkMaze(row, col, p, x, y + 1))
		{
    
    
			inputPath(right);
			p[pos] = successPath;
			return  true;
		}
	}
	if (isLegal(x + 1, y, row, col, p))	//下
	{
    
    
		//printMaze(p,row,col);
		p[pos] = visited;
		if (walkMaze(row, col, p, x + 1, y))
		{
    
    
			inputPath(down);
			p[x * col + y] = successPath;
			return true;
		}
	}
	if (isLegal(x, y - 1, row, col, p))	//左
	{
    
    
		//printMaze(p,row,col);
		p[pos] = visited;
		if (walkMaze(row, col, p, x, y - 1))
		{
    
    
			inputPath(left);
			p[pos] = successPath;
			return  true;
		}
	}
	p[pos] = visited;
	return false;	//无路可走,该条路径不行
}

//自动寻找起点,可以自行选择是否调用
void findStart(unsigned char* p, int row, int col, int* x, int* y)
{
    
    
	for (int i = 0; i < row; i++)
	{
    
    
		for (int j = 0; j < col; j++)
		{
    
    
			if (p[i * col + j] == start)
			{
    
    
				*x = i;
				*y = j;
				return;
			}
		}
	}
}

int main()
{
    
    
	int row = 15, col = 15, x = 1, y = 1;	//行和列,起点坐标
	unsigned char* Maze = (unsigned char*)malloc(row * col);	//分配空间
	inputMaze(Maze, row, col);		//输入迷宫
	printMaze(Maze, row, col);		//打印迷宫
	walkMaze(row, col, Maze, x, y);		//走迷宫
	Maze[x * col + y] = start;		//矫正起点字符
	printMaze(Maze, row, col);		//打印迷宫
	printPath();					//打印路径
	free(Maze);						//释放空间
	system("pause");
	return 0;
}


operation result:
insert image description here

Get the path: RRRDRRRRRRRRDDDDRDDDD, get flag
flag{ae2de0be8285f69db701d4dba8721a40} after 32-bit lowercase md5

2. babypython


At the beginning of this question, I thought about whether the bytecode in the txt could be converted into the binary bytecode of the pyc file. The script obtained by asking gpt is not good, so I can only use the bytecode honestly (fortunately, there is gpt) gpt assists in analysis, but it should be noted that some obfuscated codes have no practical effect, you can skip them, keep the key codes and let gpt integrate the analysis

1. Simple analysis of bytecode

You can search the flag to quickly locate the key code. There are some initialization operations above the key code
insert image description here
. Traversing the flag, the element XOR 8 is saved to the value array
insert image description here
Garbage code, and it also prompts "This line will never be executed"
insert image description here
to traverse the value array, and the element +3
insert image description here
is followed by There are some garbage codes, which are useless operations and some jumps, like here, it jumps to 1362 and
insert image description here
jumps to the end with base64 encoding processing (after processing, the string must be reversed)
insert image description here
to replace character processing.
insert image description here
You can see the output value at the end of the file
insert image description here

2. gpt analysis

Here, the useful part of the bytecode is intercepted for gpt analysis, and then let him give the python code, and then integrate each code segment to get the result of the program logic
insert image description here

3. Program logic

In short, the main logic is as follows:

  1. Traverse the flag, save each element and 8 XORs into the value
  2. Traversing the value, saving each element to output after +3
  3. Base64 encode the output, and then reverse to get obfuscated_output
  4. Replace some characters in obfuscated_output
    Final output =1nb0A3b7AUQwB3b84mQ/E0MvJUb+EXbx5TQwF3bt52bAZncsd9c
import base64

flag = '************************************'
value = ''
output = ''
i = 0
while i < len(flag):
    temp = ord(flag[i]) ^ 8
    value += chr(temp)
    i += 1
    
for j in range(len(flag)):
    temp = ord(value[j]) + 3
    output += chr(temp)
obfuscated_output = base64.b64encode(output.encode()).decode()[::-1]
obfuscated_output = obfuscated_output.replace('g', '1').replace('H', '3').replace('W', '9')
print(obfuscated_output)

4. Problem-solving script

import base64
Encoded= '=1nb0A3b7AUQwB3b84mQ/E0MvJUb+EXbx5TQwF3bt52bAZncsd9c'
Encoded= Encoded.replace('1', 'g').replace('3', 'H').replace('9', 'W')
decoded = base64.b64decode(Encoded[::-1])
for i in decoded:
    print(chr((i- 3)^8),end='')

flag{5dcbafe63fbf3b7d8647c1aee650ae9c}

3. Bad Coffee

1. Related articles

At the beginning, I couldn't see how to do this question, and then I checked the js reverse obfuscation method on the Internet and
found several articles:

  1. [JavaScript Reverse] AST Technology Deobfuscation
  2. Use javascript-obfuscator to confuse js files
    , but the article only briefly introduces it, and does not give a solution to de-obfuscation

2. Deobfuscation

Later found the tool:

  1. JS reverse | ob confused one-click restoration tool This is a local tool, downloaded but not used yet
  2. The online tool for deobfuscation test version
    is very easy to use . Just copy the code in, then select mode 1, and check all the options.
    insert image description here
  3. Deobfuscation code:
function xxx(_0x53b7bb, _0x590286) {
    
    
  return _0x53b7bb ^ _0x590286;
}

function enc(_0x4bda4c) {
    
    
  var _0x8ff1dd = [],
      _0x6aca75 = [233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8];

  for (let _0x7bc200 = 0; _0x7bc200 < 42; _0x7bc200++) {
    
    
    _0x8ff1dd[_0x7bc200] = xxx(_0x6aca75['at'](_0x7bc200), _0x4bda4c["charAt"](_0x7bc200)['charCodeAt']());
  }

  for (let _0x4f674a = 0; _0x4f674a < 42; _0x4f674a++) {
    
    
    _0x8ff1dd[_0x4f674a] = xxx(_0x8ff1dd['at'](_0x4f674a), _0x6aca75['at'](41 - _0x4f674a));
  }

  console["log"](_0x8ff1dd);
  return _0x8ff1dd;
}

function fff() {
    
    
  var _0xe4960c = "flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}",
      _0x55dae6 = enc(_0xe4960c),
      _0xbb5ecd = [135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156];

  for (let _0x37df9d = 0; _0x37df9d < 42; _0x37df9d++) {
    
    
    if (_0x55dae6['at'](_0x37df9d) != _0xbb5ecd['at'](_0x37df9d)) {
    
    
      console["log"]("Error");
      return;
    }
  }

  console["log"]("YES");
  return;
}

fff();

3. Problem-solving script

Code after manually modifying variable names and function beautification:

function xor(a, b) {
    
    
    return a ^ b;
  }
  function enc(data) {
    
    
    var result = [],
        buffer = [233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8];
    for (let j = 0; j < 42; j++) {
    
    
      result[j] =xor(buffer['at'](j), data["charAt"](j)['charCodeAt']());
    }
    for (let k = 0; k < 42; k++) {
    
    
      result[k] =xor(result['at'](k), buffer['at'](41 - k));
    }
    console["log"](result);
    return result;
  }
  function main() {
    
    
    var flag = "flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}",
        encodeFlag = enc(flag),
        data = [135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156];
    for (let i = 0; i < 42; i++) {
    
    
      if (encodeFlag['at'](i) != data['at'](i)) {
    
    
        console["log"]("Error");
        return;
      }
    }
    console["log"]("YES");
    return;
  }
  main();

It is easy to see that it is a simple XOR and comparison operation, and the problem-solving script is also very simple:

Encoded = [135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156]
array = [233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8]
for i in range(42):
    print(chr(Encoded[i]^array[41-i]^array[i]),end='')

flag{I_c0uld_neu3r_undeRstand_jvaVs3rIpt!}

四. Web&Assembly

1. View the web page in the local environment

Use the command python -m http.server -b localhost in the folder where index.html is located to open the local environment.
Please add a picture description
Enter localhost:8000 in the browser to view the content of the webpage, and give a little hint
Please add a picture description

You can use jeb or ghidra to analyze the wasm file directly, or you can use the wabt tool to decompile it and then analyze it

2. jeb&ghidra analyzes wasm files

jeb should pay attention to the path name (because the folder name Web&Assembly has the symbol &, jeb fails to open the file)

main function

Please add a picture description

The f8 function analyzed by jeb is ugly, use ghidra to analyze, first download the plugin ** ghidra-wasm-plugin **

After downloading the zip file corresponding to the ghidra version, open file>install extensions in ghidra and click the + sign to select the zip file

官方文档安装指导如下:
The easiest way to install the plugin is as a Ghidra extension. Grab a release that is compatible with your version of Ghidra - for example, if you’re using Ghidra 10.0.4, download the file beginning with ghidra_10.0.4_PUBLIC. You don’t need to unzip the file: simply launch Ghidra, go to “File -> Install Extensions”, select the + icon, and select the zip file. Restart Ghidra to load the extension and you should be good to go. Note: if you upgrade your version of Ghidra, you will need to upgrade your plugin too.

check function (ie f8 function)

uint check(char *str,char *input,int hash)

{
    
    
  uint uVar1;
  size_t strLen;
  int ii;
  int k;
  int j;
  undefined8 data;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  uint i;
  uint judge1;
  undefined8 table;
  undefined8 local_28;
  char local_20;
  int Hash;
  char *Input;
  char *Str;
  uint flagVar;
  bool judge2;
  
                    /* 0123456789abcdef */
  local_20 = s_0123456789abcdef_ram_00010000[16];
  local_28 = s_0123456789abcdef_ram_00010000._8_8_;
  table = s_0123456789abcdef_ram_00010000._0_8_;
  Hash = hash;
  Input = input;
  Str = str;
  strLen = strlen(str);
  if (strLen < 8) {
    
    
                    /* 退出 */
    flagVar = 0;
  }
  else {
    
    
                    /* 每次处理8个字符 */
    judge1 = 0;
    for (i = 0; uVar1 = i, strLen = strlen(Input), uVar1 < strLen; i = i + 8) {
    
    
      local_48 = 0;
      local_50 = 0;
      local_58 = 0;
      data = 0;
      for (j = 0; j < 8; j = j + 1) {
    
    
        *(uint *)((int)&data + j * 4) = (int)(char)(Str[j] ^ Input[j + i]) & 0xff;
                    /* 异或 */
      }
      for (k = 0; k < 114; k = k + 1) {
    
    
                    /* xor */
        XorPlus(&data,0,1,2,3);
        XorPlus(&data,4,5,6,7);
        XorPlus(&data,0,1,4,5);
        XorPlus(&data,2,3,6,7);
      }
      for (ii = 0; ii < 8; ii = ii + 1) {
    
    
        judge2 = true;
                    /* 转base16并比较 */
        if (judge1 == 0) {
    
    
          judge2 = *(char *)((int)&table + *(int *)((int)&data + ii * 4) / 16) ==
                   *(char *)(Hash + (ii + i) * 2);
        }
        judge1 = 1;
        if (!judge2) {
    
    
          judge1 = (uint)(*(char *)((int)&table + *(int *)((int)&data + ii * 4) % 0x10) ==
                         *(char *)(Hash + (ii + i) * 2 + 1));
        }
      }
    }
    flagVar = judge1;
  }
  return flagVar;
}

XorPlus

void XorPlus(int pdata,int num1,int num2,int num3,int num4)

{
    
    
  uint *tmp;
  
  tmp = (uint *)(pdata + num4 * 4);
  *tmp = *tmp ^ *(int *)(pdata + num1 * 4) + *(int *)(pdata + num3 * 4);
  tmp = (uint *)(pdata + num4 * 4);
  *tmp = *tmp & 0xff;
  tmp = (uint *)(pdata + num3 * 4);
  *tmp = *tmp ^ *(int *)(pdata + num1 * 4) + *(int *)(pdata + num2 * 4);
  tmp = (uint *)(pdata + num3 * 4);
  *tmp = *tmp & 0xff;
  tmp = (uint *)(pdata + num2 * 4);
  *tmp = *tmp ^ *(int *)(pdata + num3 * 4) + *(int *)(pdata + num4 * 4);
  tmp = (uint *)(pdata + num2 * 4);
  *tmp = *tmp & 0xff;
  tmp = (uint *)(pdata + num1 * 4);
  *tmp = *tmp ^ *(int *)(pdata + num2 * 4) + *(int *)(pdata + num4 * 4);
  tmp = (uint *)(pdata + num1 * 4);
  *tmp = *tmp & 0xff;
  return;
}


Decryption script (refer to https://jonathanbest7.github.io/ of this master)

def XorPlus(enc, a, b, c, d):
    enc[a] ^= (enc[b] + enc[d]) & 0xFF # &0xff的作用是保证在字节型范围内
    enc[b] ^= (enc[c] + enc[d]) & 0xFF
    enc[c] ^= (enc[a] + enc[b]) & 0xFF
    enc[d] ^= (enc[a] + enc[c]) & 0xFF
    return enc

def decryption(enc):
    for i in range(0x72):
        enc = XorPlus(enc, 2, 3, 6, 7)
        enc = XorPlus(enc, 0, 1, 4, 5)
        enc = XorPlus(enc, 4, 5, 6, 7)
        enc = XorPlus(enc, 0, 1, 2, 3)
    key = b'114!514!'
    for i in range(len(enc)):
        enc[i] = enc[i] ^ key[i]
    return enc
            

hex_string = "91fba5ccfef6e0905eeeb47940d25543c286b10de778fbb268ab7580414c0758"
enc = bytes.fromhex(hex_string)

flag = b''
for i in range(0, len(enc), 8):
    flag += decryption(bytearray(enc[i:i+8]))#每次处理8个字节
print(flag)

3. Wabt tool decompilation (failure to reproduce)

Refer to wasm reverse - (Geek Challenge 2021wasm

sudo apt install wabt to install wabt

wasm2c index.wasm -o index.c can be decompiled to get .c and .h files

Put the decompiled files and the wasm-rt.h, wasm-rt-impl.c, and wasm-rt-impl.h files in the wabt project into the same folder

The wabt project can be downloaded directly from github https://github.com/WebAssembly/wabt, and the file can be found in the wabt-main/wasm2c folder after decompression

Use the command gcc -c index.c -o index.o to compile but not link to generate .o files

After the .o file is generated, you can use ida to analyze it (the official wp also does this). I don’t know why the generation failed here, so I won’t go into details

Please add a picture description

5. Reference articles

  1. Shaanxi Provincial Competition Reverse AK

  2. The 3rd Shaanxi Provincial Competition REVERSE

  3. [ WASM reverse analysis ]

  4. Learn Webassembly reverse analysis method step by step

  5. WebAssembly/wabt

  6. A Wasm reverse static analysis method

6. Summary

  1. The overall feeling of this competition is very good. It is much better than going to jail in the Black Shield Cup and the National Competition a few days ago. Although some topics have not been seen before, you can find relevant articles and tools by checking the Internet
  2. We must also pay attention to the level of theoretical knowledge, and we cannot be script kiddies. Although there are ready-made tools for these topics, we still need to understand the relevant principles, otherwise we will be stunned when we change it next time. For example, this babypython used to be done with tools or The script directly works on the pyc file. This time, it can only gnaw the bytecode raw (fortunately, it has the powerful analysis ability of gpt), and there are even a lot of confusion and jump codes, which also exercise the ability to gnaw the bytecode.
  3. On the other hand, personal experience is still insufficient, and we must continue to do more questions, compete more, repeat more, and work hard to make up for it

Guess you like

Origin blog.csdn.net/OrientalGlass/article/details/131025651