-
Improper Null Termination (SSP)Hack/Pwnable 2015. 11. 20. 05:34
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
int size;
char buf [100];
char line [10];
setlinebuf(stdout);
fgets (buf, sizeof (buf),stdin);
size = atoi (buf);
fgets(buf, sizeof (buf),stdin);
strncpy (line, buf, size);
puts (line);
gets (line);
puts (line);
return 0;
}
소스코드이다. 컴파일을 하자.
$ gcc test.c
소스는 별거없이 입력받는값을 변수에넣고 출력시키는 프로그램이다.
(gdb) disas main
Dump of assembler code for function main:
0x000000000040077d <+0>: push %rbp
0x000000000040077e <+1>: mov %rsp,%rbp
0x0000000000400781 <+4>: push %rbx
0x0000000000400782 <+5>: sub $0x98,%rsp
0x0000000000400789 <+12>: mov %fs:0x28,%rax
0x0000000000400792 <+21>: mov %rax,-0x18(%rbp)
0x0000000000400796 <+25>: xor %eax,%eax
0x0000000000400798 <+27>: mov 0x2008d1(%rip),%rax # 0x601070 <stdout@@GLIBC_2.2.5>
0x000000000040079f <+34>: mov %rax,%rdi
0x00000000004007a2 <+37>: callq 0x400630 <setlinebuf@plt>
0x00000000004007a7 <+42>: mov 0x2008ca(%rip),%rdx # 0x601078 <stdin@@GLIBC_2.2.5>
0x00000000004007ae <+49>: lea -0x80(%rbp),%rax
0x00000000004007b2 <+53>: mov $0x64,%esi
0x00000000004007b7 <+58>: mov %rax,%rdi
0x00000000004007ba <+61>: callq 0x400650 <fgets@plt>
0x00000000004007bf <+66>: lea -0x80(%rbp),%rax
0x00000000004007c3 <+70>: mov %rax,%rdi
0x00000000004007c6 <+73>: callq 0x400680 <atoi@plt>
0x00000000004007cb <+78>: mov %eax,-0x94(%rbp)
0x00000000004007d1 <+84>: mov 0x2008a0(%rip),%rdx # 0x601078 <st---Type <return> to continue, or q <return> to quit---
din@@GLIBC_2.2.5>
0x00000000004007d8 <+91>: lea -0x80(%rbp),%rax
0x00000000004007dc <+95>: mov $0x64,%esi
0x00000000004007e1 <+100>: mov %rax,%rdi
0x00000000004007e4 <+103>: callq 0x400650 <fgets@plt>
0x00000000004007e9 <+108>: mov -0x94(%rbp),%eax
0x00000000004007ef <+114>: movslq %eax,%rdx
0x00000000004007f2 <+117>: lea -0x80(%rbp),%rcx
0x00000000004007f6 <+121>: lea -0x90(%rbp),%rax
0x00000000004007fd <+128>: mov %rcx,%rsi
0x0000000000400800 <+131>: mov %rax,%rdi
0x0000000000400803 <+134>: callq 0x400600 <strncpy@plt>
0x0000000000400808 <+139>: lea -0x90(%rbp),%rax
0x000000000040080f <+146>: mov %rax,%rdi
0x0000000000400812 <+149>: callq 0x400610 <puts@plt>
0x0000000000400817 <+154>: lea -0x90(%rbp),%rax
0x000000000040081e <+161>: mov %rax,%rdi
0x0000000000400821 <+164>: callq 0x400670 <gets@plt>
0x0000000000400826 <+169>: lea -0x90(%rbp),%rax
0x000000000040082d <+176>: mov %rax,%rdi
0x0000000000400830 <+179>: callq 0x400610 <puts@plt>
0x0000000000400835 <+184>: mov $0x0,%eax
0x000000000040083a <+189>: mov -0x18(%rbp),%rbx
---Type <return> to continue, or q <return> to quit---
0x000000000040083e <+193>: xor %fs:0x28,%rbx
0x0000000000400847 <+202>: je 0x40084e <main+209>
0x0000000000400849 <+204>: callq 0x400620 <__stack_chk_fail@plt>
0x000000000040084e <+209>: add $0x98,%rsp
0x0000000000400855 <+216>: pop %rbx
0x0000000000400856 <+217>: pop %rbp
0x0000000000400857 <+218>: retq
End of assembler dump.
(gdb)
디스어셈블한 결과이다.
SSP가 추가되어있는것이 보인다. puts 함수 다음에 브레이크포인트를 걸고 실행해보자
(gdb) b *main+154
Breakpoint 1 at 0x400817
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/songsari/a.out
5
AAAAA
AAAAA
프로그램이 정상적으로 돌아간다. 한번 스택을 확인해보자
0x7fffffffe956: 0xe5200000 0x0005f7ff 0x41410000 0x7f414141
0x7fffffffe966: 0xe9800000 0x7fffffff 0x41410000 0x0a414141
우리가 입력한값이 고스란히 잘 들어가있다.
(gdb) x/x $rbp-0x18
0x7fffffffe9d8: 0x5ad7ff00
요길보면 카나리가 존재하는것을 볼수있다.
랜덤으로 바뀌는거까지 확인해보면 카나리가맞다.
우리는 strncpy함수를 가지고있는데, 원본 문자열이 지정된 길이를 초과하면 널에서 끊기지않는다.
그래서 코드에서 문자열끝에 널을 세팅하지않으면 그앞에있는데이터까지 처리된다.
이것이 바로 널을 따로 지정해주지않아 발생하는 Improper Null Termination이다.
카나리는 하위 1바이트가 널이기때문에 이를 충족한다. ( strcpy 함수등에의한 읽기 및 쓰기를 방지하기위해 널이 존재)
우리는 여기서 릭을 해올수있다는것을 알수있다.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/songsari/a.out
11
AAAAAAAAAAA
AAAAAAAAAAA??
Breakpoint 1, 0x0000000000400817 in main ()
(gdb)
뭔가가 릭이된다는것을 알수있다.
릭이 됨과 동시에 카나리를 확인해보면 A하나가 하위 1바이트인 널을 덮는것을 확인할수있다.
SSP가 바뀌는것을 감지하여 프로그램이 메세지를 띄우고 종료하지만 스택오버플로우할수있는곳이있다면 이 SSP를 릭할수있있다는것이 해당 기법의 특징이다.
gets에서 오버플로우가 터지기때문에이를 이용한다.
'Hack > Pwnable' 카테고리의 다른 글
FPO(Frame Pointer Overwrite] (0) 2015.12.02 Sigreturn Oriented Programming(SROP) (0) 2015.11.24 puts 어셈블리 프로그래밍 (0) 2015.10.06 어셈블리 프로그래밍 (0) 2015.10.05 SSP Leak Prob (0) 2015.09.30