5un9hun
5un9hun Have A Nice Day!

ARM Exploit Analysis

ARM Exploit Analysis

이 역시 CTF 문제 풀다가 ARM Exploit에서 막혀서 공부할 겸 작성했다. 그냥 대충 정리했는데 나중에 더 상세하게 정리할 예정이다.

1. 레지스터 구성

  1. R0 ~ R12
  2. SP (R13)
  3. LR (R14)
  4. PC (R15)
  5. PSR

32bit

  • R0 = 주로 return 값
  • R0 ~ R3 = arg1 ~ arg4 값
  • R7 = ebp; thumb mode에서는 syscall number
  • R13 = SP (Stack Pointer)
  • R14 = LR (Linked register)
  • R15 = PC (Program Counter == eip)
  • PSR : flag를 담고 있음.
    • SPSR = 상태 레지스터
    • CPSR = 현재 상태 레지스터

2. Calling Convention (함수 호출 규약)

Argument

함수를 호출할 때 인자는 R0 ~ R3 레지스터를 이용하고, 이 후 인자는 스택에서 사용한다.

ARM Mode

ARM Mode는 32bit 명령어 셋을 제공한다.

ARM Instruction 를 사용하는데 이는 처리속도를 우선시할 때 사용한다.

Thumb Mode

Thumb Mode는 16bit 명령어 셋을 제공한다.

Thumb Instruction 를 사용하는데 코드 길이를 줄일 때 사용한다. (쉘코드 작성에 유리함.)

SP에 1을 더해서 분기를 하면 Thumb Mode로 진입한다. (주소는 짝수이기 때문에 홀수가 되면 T bit가 1이 되어서 Thumb Mode로 진입함)

Assembly

  • LDR : 스택에서 값 가져옴
    • [LDR 레지스터, 메모리]
  • STR : 스택에 값 저장(방향 →)
    • [STR 레지스터, 메모리]
  • MOV : 값 이동 (상수는 #이 붙음)
  • CMP : 비교 연산
  • TST : test 명령어
  • B : PC를 lebel로 변경한다.
    • [B addr] ⇒ jump addr과 같음.
  • BL : 서브루틴 분기 시 사용한다. LR 레지스터에 현재 명령어의 다음 명령어 주소 저장. (x86에서 ret주소 저장)
    • [BL addr] ⇒ Call addr과 같다.
  • BX : ARM Mode/Thumb Mode로 전환하면서 분기
  • BLX : ARM Mode/Thumb Mode로 전환하면서 서부 루틴 분기

조건형

  • 주로 B 명령어와 결합되어 사용된다.
  • EQ, NE, GE, GT, LE, LT, MI, PL, AL … 등등

이 외에도 많이 있다.

ARM에서는 CALL과 RET이 없다.

대신 다음과 같이 대체된다.

  • CALL = BLX (Branch with LR)
  • RET = BX lr or pop {pc}

3. Cross compiler

ARM 컴파일을 위한 Cross compiler과 원활한 디버깅을 위해서 gdb-multiarch 를 설치해야한다.

cross compiler

1
2
3
4
5
6
7
$ apt-cache search gcc | grep arm | grep gnueabihf | grep "gcc-8-arm-linux-gnueabihf - GNU C compiler (cross compiler for armhf architecture)"


$ apt-get install gcc-8-arm-linux-gnueabihf

$ /usr/bin/arm-linux-gnueabihf-gcc-8 -no-pie -o ./abc abc.c
$ qemu-arm-static -L /usr/arm-linux-gnueabihf/ ./abc

gdb-multiarch

1
2
3
4
5
6
7
$ apt-get install gdb-multiarch

#1234 포트로 abc 파일 실행 대기
$ qemu-arm-static -L /usr/arm-linux-gnueabi/ -g 1234 ./abc

$ gdb-multiarch ./abc
    -> target remote:1234 로 연결 후 디버깅

pwntools를 이용하여 디버깅 하는 법

pwntools 에서 별도로 gdb.attach 하지 않아도 된다. 다음처럼 타겟 프로그램을 작성하고, 다른 터미널에서 target 포트에 붙으면 된다.

1
r = process("qemu-arm-static -L /usr/arm-linux-gnueabi/ -g 1234 ./antidote".split(" "))

다른 터미널

1
2
gdb-multiarch ./abc
    -> target remote:1234 #이후 디버깅

Untitled

4. 익스플로잇 기법

Return To Shellcode

x86 아키텍쳐와 똑같다. 기본적으로 Stack Buffer Overflow 취약점이 발생해야 트리거할 수 있다. 또한, NX bit 보호 기법이 걸려있지 않아야한다.

ret 위치에 쉘코드를 담은 주소를 넣고, 쉘코드를 호출하는 방식이다.

Untitled

RTL(Return To Library)

x86 아키텍쳐에서는 함수 호출 규약에서 인자를 전달하는 방식이 스택이다보니 특정 레지스터에 값을 담는 가젯이 필요없지만, arm에서는 x64 아키텍쳐와 유사하게 가젯이 필요하다.

Untitled

CALL CHAIN attack

x86 아키텍쳐에서는 libc 함수를 호출하고 내부적으로 RET (pop eip, jmp eip)를 하기 때문에 다음 가젯으로 Chain을 할 수 있지만, ARM에서는 복귀를 lr 레지스터에 있는 값으로 북귀하기 때문에 Chain이 불가능하다.

따라서 좋은 가젯을 찾거나, 스택의 값으로 lr 을 변조시키는 가젯을 찾아야한다.

추후 조사 예정

ROP(Return Oriented Programming)

ASLR 기법때문에 library 주소를 모를 때 ROP 기법을 이용한다. 주로 출력 함수로 leak을 하고, system 함수를 콜한다.

RTC(Return To CSU)

ROP와 목표는 같지만, __libc_csu_init 함수에서 가젯을 이용하는 기법이다. 마땅히 쓸만한 가젯이 없을 때 이 기법을 이용해서 우회할 수 있다.

1번째 가젯과 3번째 가젯은 __libc_csu_init 함수에 존재하고, 2번째 가젯은 .init_proc 에 존재한다.

3번째 가젯에서 R6 == R9로 세팅하면 다음 ROP를 진행할 수 있다. 약간 바이너리마다 어셈이 다른것 같기도 하다. 유동적으로 사용하면 된다. R3 레지스터가 필요하지 않다면 2번째 가젯은 필요없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//first gadget
POP     {R4-R10,PC}

//second gadget
POP     {r3, pc}

//third gadget
MOV     R0, R10
MOV     R1, R8
MOV     R2, R7
BLX     R3
CMP     R4, R6
BNE     loc_~       ; => POP     {R4-R10,PC}

or

LDR     R12, [R4,#4]
MOV     R0, R10
MOV     R1, R8
MOV     R2, R7
BLX     R12
CMP     R6, R9
ADD     R4, R5, #4
BNE     loc_~       ;  => POP     {R4-R10,PC}

FSB(Format String Bug) 등등 이 외에도 많은 공격이 존재한다. 나중에 더 정리할 예정

comments powered by Disqus