Launching Calculator with a Buffer Overflow
Reproducing a Windows buffer overflow end to end, using mona.py to find the offset and land a payload.
윈도우 어플리케이션 버그헌팅을 배우기 위해 추천받은 유튜버 분을 따라 실습을 하려고 한다. 많은 부분을 생략하지만, 전체 순서를 기억해 두기 위해 정리한다.
참고한 영상: https://www.youtube.com/watch?v=gdtmbdbcq7U
퍼저는 안 돌리고, 그냥 보안 이슈로 윈도우 XP 에서 할 것이기 때문에 먼저 XP image 와 VMware tools 를 깔았다.
윈도우즈 애플리케이션 취약점 분석 입문부터 활용까지 | 보안프로젝트 - 인프런 윈도우 애플리케이션 방어기법 및 우회 공격에 대해 기본 학습부터 활용 방법, 최신 공격 사례 분석까지 다루고 있다.
유튜버 분은 여기서 따왔다고 한다. 나중에 봐도 괜찮을듯.
우선 취약점 익스플로잇 대상 프로그램은: Easy RM to MP3 Converter 2.7.3.700 이다.
파이썬으로 크래시를 확인할 파일을 만들어줬다. Olly debug setting 을 갖추고 다시 돌려보면 디버그 옵션이 나오는데, eip 를 보면 eip address 가 41414141 (파일 인풋 아스키 값이 0x41임) 로 덮어써진 것을 볼 수 있다.
참고로 eip 는 컴퓨터가 다음에 실행할 주소이다. 즉 오프셋을 디버거로 잘 분석해 인풋을 넣으면, 다음 실행주소를 원하는 주소로 바꿔 쓸 수 있다는 것이다. (즉 흐름을 망가뜨리는 취약점이 있음을 알 수 있음)
맞춰 놓은 세팅
디버거 레지스터 창. 오프셋을 알아내기 위해서 편하게 Immunity Debugger 를 사용하겠다. 이뮤니티 디버거는 다른 디버거들보다 익스플로잇을 좀 더 편하게 할 수 있게 중점이 맞춰져 있다.
이뮤니티 디버거를 다운로드 해서 파이커맨드로 따라가면, 모나라는 익스플로잇 툴이 제공된다. 이뮤니티 디버거에 !mona 를 통해서 mona 를 실행해보자.
mona 라는 익스플로잇 툴에 대해서는 따로 설명이 없어 정리해 둔다.
mona.py는 익스플로잇 개발(특히 스택 버퍼 오버플로우 분석)을 도와주는 Immunity Debugger용 플러그인입니다. Corelan Team이 만든 도구로, 보안 리서치/CTF/OSCP 같은 교육 과정에서 표준처럼 쓰입니다.
기본 개념: 왜 “패턴”이 필요한가
이전에 말씀하신 "A"*30000 같은 코드로 프로그램을 크래시시키면, EIP(다음에 실행할 명령어를 가리키는 레지스터)가 0x41414141 (전부 A)로 덮어써집니다. 문제는 — “A”가 30000개 중 정확히 몇 번째 A가 EIP를 덮었는지 알 수가 없다는 겁니다. 전부 똑같은 문자니까요.
mona.py의 패턴 생성 / 오프셋 찾기 기능
- 유니크 패턴 만들기
!mona pattern_create 30000
이렇게 하면 “A” 대신 Aa0Aa1Aa2Aa3... 처럼 4바이트마다 겹치지 않는 고유한 패턴을 만들어줍니다. 이게 De Bruijn 시퀀스라는 수학적 방법입니다.
- 크래시 후 오프셋 찾기
이 패턴 문자열로 다시 크래시를 일으키면, EIP에는 그 패턴 중 특정 4바이트 조각이 남습니다. 예를 들어 EIP가 0x69413969 같은 값이 됐다고 하면:
!mona pattern_offset 69413969
이렇게 입력하면 mona가 “이 4바이트 조각은 패턴의 정확히 몇 번째 바이트였는지” 계산해줍니다. 그럼 “아, EIP를 덮으려면 정확히 4113바이트가 필요하구나” 같은 걸 알 수 있는 거죠.
왜 이게 중요한가?
버퍼 오버플로우 익스플로잇을 만들 때 보통 이런 구조를 씁니다:
[정확한 오프셋만큼의 채우기 바이트] + [EIP를 원하는 주소로 덮기] + [실행시킬 페이로드]
오프셋을 정확히 모르면 EIP를 원하는 값으로 정밀하게 컨트롤할 수 없습니다. "A"*30000 으로는 “크래시가 난다”는 것만 알 뿐, “몇 바이트째에서 EIP가 덮이는지”는 알 수 없어서 다음 단계로 못 넘어갑니다.
mona.py의 다른 유용한 기능들 (참고로)
!mona modules 로드된 DLL들의 보호 기법(ASLR, DEP 등) 여부 확인
!mona find 메모리에서 특정 opcode(예: jmp esp) 패턴 검색
!mona bytearray bad character 확인용 바이트 배열 생성
… 라고 하시네요…?
이렇게 패턴을 명령어로 크리에이트 함!
우린 많이 필요하니까, 만 개 하면 다 뜨지는 않고 txt 파일에 저장된다. 원래 삼만 개를 넣었는데 왜 만 개만 만드냐 할 수 있는데, 유튜버 (슨생님) 이 말한 바로는 만 개를 넘으면 새로운 패턴을 만드는 데 한계가 있어 패턴이 나중에 잘 탐지가 안 된다 하셨다.
그래서 3만 개 모두 다 패턴으로 채우기 보다는, 걸릴 확률이 좀 더 적은 2만 개를 앞에 A 로 채우고 뒤에 패턴을 또 만 개 추가하는 식으로 페이로드를 짰다.
대충 이렇게 짜주었다.
Pattern Hu3H (0x48337548) found in cyclic pattern at position 6069
라고 나온다. 즉 오프셋은 6069. 하지만 2만이 껴있었기 때문에 사실상 26069 가 오프셋이다.
오프셋을 알았으면 삽입할 셸코드도 알아야 하는데, MSFvenom 이라는 Metasploit Framework에 포함된 페이로드 생성 도구가 있다. 사용할 때는:
msfvenom -p <페이로드종류> <옵션들> -f <출력형식> -o <출력파일>
이렇게 하면 리버스 쉘 셸코드를 파이썬 바이트 문자열 형식으로 뽑아주고, 이걸 앞서 만든 exploit 스크립트의 페이로드 자리에 붙여넣으면 된다.
나는 그냥 유튜브에서 카피앤페이스트 했다. 계산기를 띄우는 셸코드고, PoC (proof of concept) 이다.
[20바이트 NOP] + [실제 셸코드(buf)] + [30,000바이트의 A]
의미: 공격에 필요한 모든 요소(NOP, 셸코드)를 맨 앞에 미리 배치해 두고, 그 뒤를 엄청나게 긴 “A”로 채워서 프로그램이 감당할 수 있는 버퍼 크기를 완전히 넘기도록(Overflow) 만듭니다.
NOP는 “No Operation”의 약자로, CPU가 실행해도 특별한 동작을 하지 않고 다음 명령으로 넘어가는 것이다. 취약점 연구 자료에서는 실제 코드 앞에 여러 개의 NOP를 배치해 두는 경우가 있었습니다. 이렇게 하면 실행 위치가 정확히 코드 시작점이 아니더라도, NOP 구간 안 어딘가에 도착한 뒤 순차적으로 실제 코드 쪽으로 이동할 수 있습니다.
payload = payload[:offset] + "CCCC"
이 부분이 핵심입니다. 이전 단계에서 유니크한 패턴을 통해 EIP 레지스터가 변조되는 정확한 위치(오프셋 값)를 찾아냈다고 가정합니다.
payload[:offset]: 맨 앞부터 오프셋 직전까지의 문자열만 딱 잘라냅니다. 이 잘려진 데이터 안에는 이미[NOP * 20 + buf + 일부 A]가 들어가 있습니다.+ "CCCC": 잘라낸 문자열 바로 뒤에 “CCCC”를 붙입니다.
결과로는 전체 페이로드의 정확히 offset 위치(즉, 시스템이 다음 실행할 주소를 읽어오는 RET 자리)에 "CCCC" (\x43\x43\x43\x43) 가 배치됩니다.
EIP 를 보면 딱 \x43\x43\x43\x43 으로 도배된 것을 알 수 있다. 즉 저기 90 (NOP) 들의 주소를 C 대신 넣으면, 셸코드가 실행될 것이다. 한번 해봅시다.
import struct
의미: 파이썬의 struct 모듈은 가상 메모리 주소(숫자)를 시스템이 인식할 수 있는 바이너리(바이트) 형태로 변환할 때 사용합니다.
그 후 그 주소를 언팩해서 넣으면 그 NOP 이 있는 주소로 넘어가 셸코드를 실행시킵니다.
그럼 이 파일을 (테스트 5) 넣었을 때, 계산기가 뜬다. 이건 계산기를 예제로 띄운 것 뿐이고, 여러 다른 프로그램을 실행할 수 있다.















