
애드혹 온더플라이 프로토콜 생성을 위한 인메모리 최소 CPU
Harald는 인메모리 방식의 소형 고수준 CPU로, 주어진 네트워크 프로토콜(TCP/UDP) 상에서 사용될 애플리케이션 계층 프로토콜을 생성하기 위한 명령어 집합을 처리할 수 있습니다.
Harald는 OPCODE 스트림을 소비하고, 정의된 애플리케이션 계층 프로토콜에 적합하도록 페이로드에 다양한 변환을 적용합니다.
[!TIP] 왜 _Harald_일까요? 덴마크의 왕 Harald Gormsson은 그의 별명인 Blátǫnn, 즉 오늘날 우리가 발음하는 Bluetooth로 유명합니다. 이 기술은 당시 Harald가 덴마크와 바이킹 왕국을 _연결_했기 때문에 그의 별명을 따서 명명되었습니다. 그래서 저는 그의 이름(firstname)을 따서 이것을 명명했습니다.
Harald는 CPU처럼 동작합니다. 이는 동작을 정의하는 OPCODE 집합을 따릅니다. 즉, PROTOCOL입니다. Harald는 필요한 프로토콜을 즉석에서 생성하기 위해 요구되는 연산을 처리합니다.
PROTOCOL이 제공되면, Harald는 전송할 PAYLOAD를 순회하면서 각 프래그먼트마다 전체 PROTOCOL을 한 번씩 실행합니다. 즉, 페이로드를 여러 프래그먼트로 분할해야 하는 경우, Harald는 모든 프래그먼트에 동일한 프로토콜을 사용하여 현재 페이로드 프래그먼트에 대해 프로토콜에 정의된 모든 동작을 실행합니다.
Harald는 프로토콜이 값을 저장하는 데 사용할 수 있는 여러 레지스트리를 보유합니다. 이러한 레지스트리는 덧셈, 뺄셈, 인코딩 및 디코딩 등 여러 연산을 수행하는 데 사용할 수 있습니다.
Harald는 페이로드 프래그먼트를 전송할 때 수행되는 각 요청에 대한 응답을 프로토콜이 저장할 수 있도록 결과 배열을 동적으로 할당합니다.

Harald는 프로토콜로 제공될 다양한 Opcode를 지원합니다. Harald는 주어진 Opcode를 수신하면 일련의 고수준 연산을 수행합니다. 각 Opcode는 미리 정의된 길이의 미리 정의된 인수 집합을 기대합니다.
| OPCODE::Nº | Description | ARGS::Size |
|---|---|---|
| H_F_APPEND::0x01 | Appends the following N Bytes to the end payload | Size::4, NBytes::N |
| H_F_PREPEND::0x02 | Prepends the following N Bytes to the end payload | Size::4, NBytes::N |
| H_F_SPLIT::0x03 | Splits the Payload in fragments of the given size | Size::4 |
| H_F_PAYLOAD_INJECT::0x04 | Injects the Payload fragment into the End Payload | - |
| H_F_SOCK_INIT::0x05 | Initializes the socket connection | IsIPAddress:1, SSL_Enabled::1, TCP/UDP::1, [(HostLen::4,HostName:N)/IPAddress::4], Port::2 |
| H_F_SEND::0x06 | Sends the constructed End Payload | - |
| H_F_RECV::0x07 | Receives up to N Bytes | Size::4 |
| H_F_CLOSE::0x08 | Closes the socket and cleans | - |
| H_F_INJECT::0x09 | Injects the requested registry into the End Payload | Reg::1 |
| H_F_ENCODE_STR::0x0A | Encodes a Payload field to its string representation | IsReg::1, [(Payload::1, Field::1)/Reg::1], Format::1 |
| H_F_DECODE_STR::0xA0 | Decodes the contents of a registry to its numeric representation | Reg::1 |
| H_F_ENCODE_STRB64::0x0B | Encodes a Payload field to its B64string representation | Payload::1, Field::1 |
| H_F_COMPUTE_FRAG_LENGTH::0x0D | Calculates the next fragment to be sent length | - |
| H_F_SEARCH::0x0E | Searches for a given byte pattern of a given N size in a given registry | Reg::1, Size::4, NBytes::N |
| H_F_SEEK::0x0F | Moves the cursor in a given payload by a given number of bytes | Payload::1, Offset::4 |
| H_F_PUSH::0x10 | Pushes a number of bytes or the contents of a registry into another registry | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_ADD::0x12 | Adds two registries or a number to a registry | IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
다음 Opcode 스트림은 통신이 HTTP로 이해되도록 전송에 필요한 데이터를 생성합니다. 이는 연결을 시작하고, 페이로드를 HTTP 트래픽처럼 보이도록 수정하며, SSL/TLS 연결을 통해 전송하고, 마지막에 연결을 닫습니다.
H_F_SPLIT, 0x00, 0x00, 0x00, 0xFF, // Splits the Payload
H_F_APPEND, 0x00, 0x00, 0x00, 0x46, // Appends...
'P', 'O', 'S', 'T', ' ', '/', ' ', // POST /
'H', 'T', 'T', 'P', '/', '1', '.', '1', '\r', '\n', // HTTP/1.1
'H', 'o', 's', 't', ':', ' ', // Host:
'd', 'o', 'm', 'a', 'i', 'n', '.', 'c', 'o', 'm', '\r', '\n', // domain.com
'C', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n', ':', ' ', // Connection:
'c', 'l', 'o', 's', 'e', '\r', '\n', // close
'C', 'o', 'n', 't', 'e', 'n', 't', '-', // Content-
'L', 'e', 'n', 'g', 't', 'h', ':', ' ', // Length:
H_F_COMPUTE_FRAG_LENGTH, // Next Fragment's size
H_F_ENCODE_STR, H_PAY_END_PAYLOAD, H_PAY_FRAGMENT_SIZE, 'u', // FragSize encode
H_F_INJECT, H_REG_RET, // Inject the Fragment Size encoded str
H_F_APPEND, 0x00, 0x00, 0x00, 0x04, '\r', '\n', '\r', '\n', // Append Body begin
H_F_PAYLOAD_INJECT, // Inject the Payload
H_F_SOCK_INIT, 0x01, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x04, 0xD2, // Init Socket
H_F_SEND, // Send the Payload over the socket
H_F_CLOSE // Close the socket and cleanup
다음 예제는 이전 예제와 동일한 절차를 따릅니다. 그러나 페이로드를 단순히 본문으로 전송하는 대신, 프로토콜은 Harald에게 몇 가지 헤더를 설정하고 본문을 구성하여 GitHub API와 통신해 비공개 저장소의 이슈에 댓글을 게시할 수 있도록 지시합니다.
이러한 종류의 통신은 공격자가 *.github.com과 같은 신뢰할 수 있는 도메인을 사용하여 C2 서버와 통신 채널을 만드는 데 도움이 될 수 있습니다. 바로 이 예제는 AWS 리소스(sqs..amazonaws.com 아래의 SQS 큐 등), Azure 리소스(<storage_account_name>.blob.core.windows.net 아래의 blob 등)를 사용하도록 확장할 수도 있습니다.
이 예제는 STRUCTURED_FORMAT(JSON) + APPLICATION-LAYER_PROTOL(HTTPS) + PROTOCOL(TCP)을 요구하는 통신에 좋은 참고 자료입니다.
H_F_SPLIT, 0x00, 0x00, 0x00, 0xFF,
H_F_APPEND, 0x00, 0x00, 0x01, 0x85, 'P','O','S','T',' ',
'/','r','e','p','o','s','/','YOUR_GH_USER','/','YOUR_GH_REPO',
'/','i','s','s','u','e','s','/','ISSUE_NUMBER',
'/','c','o','m','m','e','n','t','s',' ',
'H','T','T','P','/','1','.','1','\r','\n', 'H','o','s','t',':','
','a','p','i','.','g','i','t','h','u','b','.','c','o','m','\r','\n',
'A','u','t','h','o','r','i','z','a','t','i','o','n',':',' '
,'B','e','a','r','e','r',' ','g','i','t','h','u','b','_','p','a','t','_',
'YOUR_GH_PAT','\r','\n',
'X','-','G','i','t','H','u','b','-',
'A','p','i','-','V','e','r','s','i','o','n',':','
', '2','0','2','2','-','1','1','-','2','8','\r','\n',
'U','s','e','r','-','A','g','e','n','t',':','
','H','a','r','a','l','d','/','0','.','1','b','\r','\n',
'A','c','c','e','p','t',':','
','a','p','p','l','i','c','a','t','i','o','n','/',
'v','n','d','.','g','i','t','h','u','b','+','j','s','o','n','\r','\n',
'C','o','n','t','e','n','t','-','T','y','p','e',':',' ',
'a','p','p','l','i','c','a','t','i','o','n','/',
'x','-','w','w','w','-','f','o','r','m','-',
'u','r','l','e','n','c','o','d','e','d','\r','\n',
'C','o','n','n','e','c','t','i','o','n',':','
','c','l','o','s','e','\r','\n',
'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ',
H_F_COMPUTE_FRAG_LENGTH,
H_F_ADD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0B, H_REG_RET,
H_F_ENCODE_STR, 0x01, H_REG_RET, 'u',
H_F_INJECT, H_REG_RET,
H_F_APPEND, 0x00, 0x00, 0x00, 0x0D, '\r', '\n', '\r', '\n', '{', '"', 'b',
'o', 'd', 'y', '"', ':', '"',
H_F_PAYLOAD_INJECT,
H_F_APPEND, 0x00, 0x00, 0x00, 0x02, '"', '}',
H_F_SOCK_INIT, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0F,
'a', 'p', 'i', '.',
'g', 'i', 't', 'h', 'u', 'b', '.', 'c', 'o', 'm', '\0', 0x01, 0xBB,
H_F_SEND,
H_F_CLOSE
빌드하려면 컴파일러가 필요합니다. GCC, Clang 또는 Microsoft의 CL과 같은 컴파일러를 사용할 수 있습니다. 또한 몇 가지 라이브러리 의존성이 있습니다:
LibSSL과 pkg-config를 설치합니다:
sudo apt install libssl-dev pkg-config
brew를 통해 OpenSSL을 설치하거나 직접 빌드 및 구성합니다.
brew install openssl
연결된 배포자 중 하나에서 OpenSSL을 받거나 직접 빌드 및 구성합니다.
저장소에는 라이브러리와 일부 테스트 바이너리를 빌드하기 위한 Makefile이 포함되어 있습니다.
Harald 라이브러리를 빌드하려면 다음을 실행합니다:
make lib
이렇게 하면 플랫폼에 따라 공유 라이브러리 libharald.so, libharald.dll 또는 libharald.dylib가 생성됩니다.
테스트 바이너리를 빌드하려면 다음을 실행합니다:
make test
이렇게 하면 플랫폼에 따라 각각의 확장자를 가진 두 개의 바이너리 test_harald와 test_harald_gh가 생성됩니다.
Harald의 기능을 테스트하려면 제공된 서버 socket_server_test.py와 https_server_test.py를 실행합니다. 이들은 각각 테스트 바이너리에서 사용할 소켓 서버와 HTTPS 서버를 생성합니다.
참고:
LD_LIBRARY_PATH를 현재 폴더로 설정해야 할 수 있습니다:LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./test_harald
HTTPS 통신을 위해서는 SSL/TLS 인증서를 생성해야 합니다(자체 서명이어도 됩니다). OpenSSL을 사용하여 다음 명령으로 생성할 수 있습니다:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
서버가 실행되면 test_harald 바이너리를 실행합니다:
./test_harald
바이너리는 제공된 서버에 연결하여 프로토콜에 지정된 미리 정의된 데이터 페이로드를 전송해야 합니다.
GitHub API 기능을 테스트하려면 test_harald_gh 바이너리를 실행하기 전에 몇 가지 준비가 필요합니다.
저장소와 이슈를 생성합니다.
test_harald_gh.c 파일에는 Harald를 사용하여 GitHub API와 상호 작용해 이슈에 댓글을 생성하는 방법의 간단한 예제가 포함되어 있습니다. 이는 GitHub 액세스 토큰이 필요하며, GitHub 계정 설정의 Developer settings -> Personal access tokens -> Fine-grained tokens에서 얻을 수 있습니다. 토큰은 새로 생성한 저장소에만 액세스하고 Read access to metadata 및 Read and Write access to issues and pull requests 권한만 갖도록 세분화하는 것이 좋습니다.
제대로 실행하려면 다음 자리 표시자 값을 수정해야 합니다:
YOUR_GH_USER: GitHub 사용자 이름.ISSUE_NUMBER: 상호 작용하려는 저장소의 이슈 번호(예: 저장소의 첫 번째 이슈에 대해 "1").YOUR_GH_REPO: 상호 작용하려는 저장소의 이름(예: "harald").YOUR_GH_PAT: GitHub 액세스 토큰.완료한 후에는 전송되는 페이로드의 크기를 필요에 맞게 업데이트해야 합니다!
test_harald_gh.c#L9에서 H_F_APPEND opcode의 4번째 매개변수는 페이로드의 크기를 나타냅니다. 페이로드 구조를 수정할 때 이 값을 그에 맞게 업데이트해야 합니다.
테스트 바이너리를 빌드하고 다음을 실행합니다:
./test_harald_gh
이렇게 하면 제공된 이슈에 댓글이 생성됩니다.
이 저장소에 포함된 소스 코드는 네트워크 필터링을 우회하고 보안 어플라이언스에 의한 탐지 위험을 줄이는 데 사용될 수 있습니다. 이 소스 코드를 사용하기 전에, 귀하의 사용이 귀하의 관할권에서 적용되는 모든 법률을 준수하는지 확인하십시오.
| H_F_SUBSTRACT::0x13 |
| Substracts two registries or a number to a registry |
| IsReg::1, [Size::4, NBytes::N]/SrcReg::1, DstReg::2 |
| H_F_READ::0x14 | Reads from a given registry up to the length stored in another registry | FromReg::1, SizeReg::1 |
| H_F_STORE::0x15 | Stores a registry value into Harald's Storage | Reg::1 |