본문 바로가기

study/CTF

[CTF] pwanable.kr_passcode (PLT와 GOT 개념 정리)

pwnable.kr의  passcode 문제 풀이

passcode 문제

문제 해석

더보기

엄마가 암호 기반 로그인 시스템을 만들라고 하셨어.
내 초기 C 코드는 오류 없이 컴파일되었어!
컴파일러 경고가 있긴 했지만 누가 신경이나 쓰겠어?

ssh 접속하기

ssh 접속 화면

1. ls -al 명령어를 사용하여 파일 및 디렉토리 리스트를 출력

: 권한(퍼미션, 허가권)을 확인 할 수 있음

출력 결과 -> 파일 종류 및 권환(퍼미션), 링크 수, 사용자(소유자), 그룹, 파일크기, 수정시간, 파일이름

ls -al 명령어 : 파일 종류 및 권환 화면

관련 내용 : https://pamooochim.blogspot.com/2015/10/blog-post_27.html

 

리눅스 파일 구조와 파일 권한 읽기, 명령어 소개

파일 구조 ls -l 명령어로 파일 목록 출력했을 때 보이는 화면 형식: {파일유형} : {파일권한} : {링크수} : {소유 계정} : {그룹명} : {파일크기} : {마지막 변경 일자} : {파일명} ex> d : rwxr-xr-x. : ...

pamooochim.blogspot.com

2. cat 명령어를 사용하여 passcode.c 파일 내용 확인하기

cat passcode.c 실행 화면

passcode1과 passcode2를 입력하면 될 것 같다.

3. ./passcode 실행하기

./passcode 실행 화면

하지만 passcode1을 입력한 후에 segmentation fault (core dumped) 가 출력되면서 실행이 중지된다.

segmentation fault (core dumped) 원인에 대한 블로그 : https://ho-j.tistory.com/8

 

[Ubuntu][해결중] segmentation fault (core dumped) error 원인

segmentation Fault는 세그멘테이션 위반, 세그멘테이션 실패라고도 하며 세그폴트로 줄여서 쓰기도 한다. 세그멘테이션 결함의 주된 원인은 다음과 같다. 1. 프로그램이 허용되지 않은 메모리 영역��

ho-j.tistory.com

4. passcode1 입력 이후에 오류가 발생하였기 때문에 login() 함수의 passcode1 입력 이후부터 확인

login() 함수의 입력 부분 화면

passcode1 과 passcde2 입력 사이에 fflush(stdin)이라는 코드를 발견하였다.

중간 주석 해석

더보기

 ha! mommy told me that 32bit is vulnerable to bruteforcing

하! 엄마가 32bit은 무작위 대입 공격에 취약하다고 하셨어.

5. fflush(stdin) 코드에 대해 조사하기

fflush() : 스트림에 할당된 버퍼를 비우는 명령

- 버퍼를 비운다는 의미는 입력버퍼/출력버퍼에 따라 다르다.

입력버퍼를 비우는 것 : 해당 내용을 완전히 삭제하고 새로운 입력을 받을 준비하는 것을 의미

출력버퍼를 비우는 것 : 버퍼의 내용을 해당 출력 스트림으로 완전히 출력시키는 것을 의미

6. 무작위로 입력값을 넣어보기

무작위로 입력값을 넣은 화면

passcode1의 입력값을 매우 길게 입력하였더니 passcode2 에 대해서 떴다.

Login Failed! 가 출력된 것을 통해 login() 함수의 passcode 비교문 부분이 실행되었다는 것을 알 수 있었다.

login()함수의 passcode 확인하는 부분 코드

7. scanf()에 문제가 있는 것을 확인함

login() 함수의 입력 부분 화면

c언어에서 scanf() 함수를 사용할 때, 변수형과 변수의 주소를 인자값으로 넣어준다. &가 생략되어 있는데, &는 변수앞에 붙으면 그 변수가 들어있는 주소를 알려주는 것이다. scanf에 &(변수) 는 사용자가 입력하는 stdin이 들어갈 곳이 그 주소가 가지고 있는 값이다. 즉, & 없이 scanf(자료형, 변수)로 사용된다면 그 변수 자체가 주소가 되며 그 주소에 입력한 값이 들어간다. 따라서 %d로 받아오는 정수들은 segmentation fault가 발생하게 된다. 그래서 passcode 숫자 입력했을 때, 오류가 발생하는 것이다.

welcom() 함수 코드 화면

이름을 입력받는 welcom() 함수 또한 scanf() 함수에 문제가 있는 것을 발견하였다.

8. welcome()함수의 name과 login() 함수의 passcode1이 들어가야할 위치 찾기

gdb를 사용해서 주소들의 위치를 찾는다.

main() 함수 어셈블리 화면
welcome() 함수 어셈블리 화면

우선, scanf() 함수 이후에 break point를 걸어준 후, 코드를 실행하여 name 입력값으로 많은 A를 입력한다. 그 후 x/40x $esp 명령어를 사용하여 스택에 저장된 값들을 출력한다.

welcome() 함수의 마지막 print() 명령어에 BP를 적용시킨 후, 실행 화면

A의 아스키 코드 값은 41로 스택에 총 100개의 A값이 넣어졌음을 알 수 있다.

welcome()과 login() 의 어셈블리 화면을 보면 입력한 값을 어느 곳에 저장하는지 알 수 있는데,

welcome()의 name은 -0x70(%ebp)에 저장되고

login() 의 어셈블리 화면

login()의 passcode1은 -0x10(%ebp)에 저장된다.

두 주소값의 차는 0x60으로 96바이트임으로,

이번에는 name에 A 96개를 입력해보았다.

96개의 A가 스택에 쌓여있음을 확인할 수 있으며 마지막 4바이트가 다른 값으로 덮어씌여졌음을 알 수 있으며 passcode1이라는것을 유추할 수 있다. 이제 passcode1의 값을 주소로 하는 곳에 원하는 값을 넣을 수 있다.

9. passcode1 자리에 원하는 값을 넣기 위해서는, passcode1 입력 후에 실행되는 fflush(stdin) 라인을 system(/bin/cat flag);가 시작되는 주소로 바꿔야한다.

passcode1 입력부분을 PLT(procedure llinkage table) & GOT(global offset table)을 사용하여 우회해야한다.

라이브러리 함수를 호출하게 되면 먼저 plt로 간 수, got로 점프를 뚜고 got에서 라이브러리의 함수 주소를 얻어오는 구조이다. fflush 함수를 호출 할때, plt에서 got로 점프하는 부분의 주소를 system("/bin/cat flag")로 바꾼다.

PLT & GOT 개념 정리

더보기
출처 https://m.blog.naver.com/PostView.nhn?blogId=ln8520nl&logNo=220695128399&proxyReferer=https:%2F%2Fwww.google.com%2F

PLT & GOT 주소

각 PLT에 어떤 것들이 들어있는지 보기 위해 x/40xi PLT주소 명령어를 해준다.

name 96바이트 + 4바이트 부분에 got주소를 주어서 if문을 넘어간다.

nop 96바이트 + got@exit(0x804a018) + system("/bin/cat flag")

system("/bin/cat flag")의 위치 화면

시스템 콜 하기 직전의 명령어의 주소가 system("/bin/cat flag") 에 해당한다. (0x080485e3)

passcode 실행 파일을 실행하여 name에 직접 입력 하여도 괜찮지만, nop 96비트를 치기엔 번거로워서 코드를 통해 입력하겠다. system("~") 의 주소는 scanf()에서 %d로 받아드리기때문에 정수로 바꿔 입력해야한다.

정답

'study > CTF' 카테고리의 다른 글

[HackCTF] Pwnable - RTL_World  (0) 2020.09.23
[HackCTF] Pwnable - Basic_BOF #1  (0) 2020.09.23
[CTF] suninatas.com_web02  (0) 2020.04.23
[CTF] reversing.kr_easy crack  (0) 2020.04.17
[CTF]wargame.kr_img recovery  (0) 2020.04.02