2024. 7. 20. 19:22ㆍInformation Security 정보보안/Vulnerability Analysis 취약점 분석
배울내용:
시스템 해킹
단위공격 기술
포맷스트링 공격 발생 원리
포맷스트링 공격 실습
포맷스트링 공격 취약점 방지 방법
메모리 취약점
포맷스트링 공격 이란?
Format String Attack
포멧스트링 2byte 공격
포멧스트링 perl 공격
beefDead
포맷스트링 공격이란?
포맷스트링 공격은 프로그램에 입력된 문자열 데이터가 명령으로 해석될 때 발생한다.
printf()와 같은 형식 문자열을 사용하는 함수에서 C와 C++과 같은 저수준 프로그래밍 언어에서 자주 발견된다.
이전 포멧스트링 1 편에 좀 더 설명 되어있으니 자세히 알고싶으면 아래의 링크로 이동하길바란다.
https://sarimus.tistory.com/143
이전시간에 Perl을 이용해 특정 바이트 문자열을 생성하고 실행할떄 아래와 같이 코드를 작성했을것이다.
`perl -e 'print "x01\x02\x03\x04", %p%p%p%p%p%12345c%n"'`
#나중에 실행할때 넣어줄떄
r <<< `perl -e 'print "x01\x02\x03\x04", %p%p%p%p%p%12345c%n"'`
그러면 실제로 리틀엔디안 방식으로 작은주소에서 큰주소로 써주면 된다.
이 부분이 헷갈리면 Buffer OverFlow 취약점을 실습한 아래의 포스팅을보면 좀더 이해하기 쉽다.
위와 같이 넣게 되면 0x04030201 로 들어가게 되는것이다.
https://sarimus.tistory.com/138
%n과 %hn은 C 언어의 printf 함수와 관련된 포맷 지정자(format specifier)들로,
포맷 문자열 공격이나 특정한 메모리 조작을 할 때 사용된다.
.
위와 같이 넣는것은 N 을 쓸떄는 4바이트에 넣는것인데 2 바이트를 쓰는걸이용해서 2번 넣을수도 있다.
예를 들어 0xbeef 만 쓸려면 쓸수있지만 만약에 0xbeefdead 가 되어버리면 4바이트에 저걸 넣을수가없다.
이럴때 Hn 으로 2 byte 씩 나누어 한자리에는 beef, 그다음자리에는 dead 를 넣을수있는데
이공격할때 조건은 주소를알아야하고 , 해당 주소들이 위치해야한다 , 그리고 스택의 주소가 2개들어가있어야며
%hn 을 2번 써야한다.
N와 Hn의 차이점
- 저장되는 데이터 타입:
- %n: int 타입의 정수 값을 저장 .
- %hn: short int 타입의 정수 값을 저장
- 메모리 사용 크기:
- %n: 4바이트 또는 8바이트(플랫폼에 따라 다름)를 사용하여 정수 값을 저장
- %hn: 2바이트를 사용하여 정수 값을 저장
공통점으로는
이들은 출력된 문자의 수를 특정 주소에 저장하는 데 사용된다
실습
코드작성
format_1.c
#include <stdio.h>
int answer = 0x9047;
void main(){
char buf[512] = {0,};
fgets(buf, 511, stdin);
printf(buf);
if(answer == 0xbeefdead){
printf("You Win!\n");
}
}
format_1.c 라는 이름으로 파일을 만든뒤 위와 같이 작성해준다
gcc -m32 -no-pie format_1.c -o format_1
그리고 컴파일 시켜준다
코드실행
그리고 실행후에 %x 를 8번 써주고 확인해봤더니 1ff 이후에는 모두 더미데이터처럼보이는게 들어가있다
일단 1ff 는 HEX 값으로 --> DEX 로 치환하면
코드에서 작성했던 buf 의 크기인걸 알수있다.
그리고 뒤에 오는 값들은 스택에 저장된 다른 정보들로, 로컬 변수, 저장된 레지스터 값, 이전 함수 호출의 리턴 주소 등이 포함될 수 있다.
이제 gdb 를 실행시켜서 자세히 보자
그리고 pd main 을했을때 130 에서는 eax 하고 beefdead 를 비교하는게 보이고
98 은 buf 를 받고 116 은 출력하는걸보면 어디서 어떤코드인지 소스코드를 보면 잘알수있다.
그러면 대체 어디서 break point 를 거는게 좋을까??
포맷 문자열 취약점 공격을 수행하려면 주로 printf 함수 호출 부분에 브레이크포인트를 설정하여 프로그램이 어떤 값을 출력하는지, 그리고 포맷 문자열이 어떤 영향을 미치는지 확인할 수 있다.
확인할 값들
- 스택 프레임:
- 스택에 있는 값들이 어떻게 변화하는지 확인
- 특히, 포맷 문자열의 주소와 그 포맷 문자열에 의해 참조될 수 있는 값들이 중요함
- 레지스터 값:
- eax, ebx, ecx, edx 등의 레지스터 값을 확인하여 함수 호출 전에 어떤 값들이 준비되있는지 확인
- 포맷 문자열:
- ebp-0x21c의 주소에 저장된 포맷 문자열의 내용을 확인
- x/s 명령어를 사용하여 해당 메모리 주소에 저장된 문자열을 확인
그럼이제 print를하고 그뒤에 cmp beefdead 를 확인하는 부분에서 break point 를 걸어보자
b *main+116 한뒤에 r 해서 실행하고 AAAA 를 넣어보자
그러면 ESP 에도 AAAA 그리고 EAX 에도 들어간게 보인다
이걸 둘다 확인해보면
뭔가 ESP 에 7번쨰의 값(제일 앞자리 주소 제외) 에 0x41414141 이 있는걸 볼수있다
이는 분명 내가 입력했던 AAAA 값이 들어간게 분명하다
그럼이제 answer 를 한번 봐준다
p &answer 와 p answer 로 주소와 주소안에 값을 가져온다
이제 주소를 알아냈고 바꿀수있는 위치와 주소안 값을 바꿀수가있다.
이러면 Format String 공격조건에 만족한다
이러면 perl 함수를 써서 BEEF 만 넣어보자
Beef 는 DEC 로는 48879 이니 이값을 perl 함수에 넣어주면된다
`perl -e 'print "\x28\xc0\x04\x08", "%488879c%7\\$n"'`
여기에 하나더 우리는 이전에 %p%p%p로 여러번써서 7번쨰를 마춰줫지만
DEC값+ c%위치숫자\\%n 하면 7번쨰에 넣을수있다
DEC값 : 12345
위치값 : 5
ex) %12345c%5\\$n
그런데 값을 확인해보니 bef3 으로 바껴있다
이러한 이유는 전과 동일하게 %n 은 앞에 출력된 길이를 나타내니 이러한 길이 (4) 도 더해줘야한다 (48879에서 4빼야함)
(BEF3 는 BEEF 에서 4 더해줘야하기 때문에 BEF3 으로 되는것이기 때문에 BEEF 를 출력하기 위해서 명령문에서는 4를 빼야함)
`perl -e 'print "\x28\xc0\x04\x08", "%488875c%7\\$n"'`
BEEF 까지 값을 넣는거는 성공했다 그러나 우리는 BEEFDEAD 를 넣어야한다
그렇기 떄문에 여기서부터는 %n 으로 4바이트를 넣는게 아닌 %hn 으로 2바이트씩 잘라서 코드에 넣어볼것이다
값 계산 및 주소 설정
- BEEF (48879)와 DEAD (57005)의 차이는 8116. 이는 포맷 문자열에서 조정할 수 있는 값이다.
주소는 낮은곳에서 높은곳으로 쓰니 아래처럼 쓰면 된다
r <<< ‘perl -e ‘print “\x2a\xc0\x04\x08\x28\xc0\x04\x08” ,”%48871c%7\\$hn%8126c%8\\$hn”’`
그러면 7번쨰에 0x0804c02a 와 8번째에 0x0804c028 이 들어간걸 볼수있고 이는 각각 beef 와 dead 로 들어가게 된다.
그리고 p answer 하면 beefdead 가 나오는걸 볼수있고 c 를 눌러서 진행시키면
위와같이 성공 플레그를 뛰우게 된다
또는 순서를 거꾸로한다면 아래와 같은 방법도 가능하다
`perl -e 'print "\x28\xc0\x04\x08\x2a\xc0\x04\x08", "%56997c%7\\$hn%57410c%8\\$hn"'`
우선 0x8040c28 에는 Dead 가 들어가는데 여기서 %n 때문에 스택의 위치에 있는 주소에 지금까지 출력된 문자의 수를 기록을 더하면
여기에서 8을 뺴줘야한다 그러면 56997 이 되게 된다
뒤에서는 56997 에 57410 값을 더하면 114,415 가 되게되고 이는 아래와같이 표시된다.
이떄 2byte 의 특성상 4byte 에 BEEF 가 들어가고 나머지 1은 들어갈 공간이 없으니 그냥 버려진다
이걸이용해 DEADBEEF를 넣을수있게 된것이다.
이렇게 성공하게 되는 것이다
정리 풀이
- 목표: 메모리의 특정 주소에 0xDEADBEEF 값을 써넣기
- 조건: 메모리 주소는 4바이트이지만, 포맷 문자열의 %hn을 사용하여 2바이트씩 나누어 값을 써넣어야 함.
단계별 해결 방법
- 주소 계산:
- 현재 주소: 0x804c028 (메모리 주소)
- 메모리 주소는 낮은 바이트부터 높은 바이트까지 2바이트씩 분할하여 쓰면 된다.
- 0x804c028에서 2바이트 낮은 주소는 0x804c028, 높은 주소는 0x804c02a.
- 포맷 문자열 작성:
- 포맷 문자열에서 0xDEAD와 0xBEEF를 각각 2바이트 단위로 쓸 수 있도록 준바.
- 문제: 0xBEEF는 0xDEAD보다 더 큰 값이므로 이를 정확히 조정해야한다.
- 포맷 문자열 조정:
- 원하는 값인 0xDEADBEEF를 메모리에 쓴다는 것은 두 개의 포맷 문자열 %hn을 이용하여 값을 쓴다는 것을 의미
- 이때 0xDEAD와 0xBEEF를 각각 메모리 주소에 쓸 때 포맷 문자열에 맞게 값을 조정해야함
- 구체적인 포맷 문자열:perl -e 'print "\x28\xc0\x04\x08\x2a\xc0\x04\x08" ,"%48879c%7$hn%8126c%8$hn"'
- 설명:
- \x28\xc0\x04\x08와 \x2a\xc0\x04\x08: 메모리 주소
- %48879c%7$hn: 첫 번째 2바이트 (0xDEAD에 맞는 값) ( 48871에서 문자열길이 +8 해준값)
- %8126c%8$hn: 두 번째 2바이트 (0xBEEF에 맞는 값)
- 설명:
- 최종적으로 다음과 같은 포맷 문자열을 사용하여 원하는 주소에 0xDEADBEEF를 써넣을 수 있다.
이 과정에서 포맷 문자열의 위치와 패딩 계산이 중요하다.
메모리 주소와 포맷 문자열의 정확한 위치를 이해하고 적용하면 성공적으로 원하는 값을 메모리에 써넣을 수 있다.
'Information Security 정보보안 > Vulnerability Analysis 취약점 분석' 카테고리의 다른 글
시스템 해킹 실습 -9 . Buffer OverFlow (버퍼 오버플로우) [3] (2) | 2024.07.23 |
---|---|
시스템 해킹 실습 -8 . Buffer OverFlow (버퍼 오버플로우) [2] (0) | 2024.07.21 |
시스템 해킹 실습 -6 . Format String Attack 포맷스트링 공격 (0) | 2024.07.19 |
시스템 해킹 실습 -5 . Type Confusion 데이터 유형 혼동 (1) | 2024.07.19 |
시스템 해킹 실습 -4 . Uninitialized 미초기화 취약점 (0) | 2024.07.18 |