Python 이미지 파일 해킹하기
System/Cybersecurity

Python 이미지 파일 해킹하기

※ 본 포스팅은 <파이썬 해킹 입문 - 공격의 언어 파이썬을 이용한 해킹 연습> (조성문 정영훈 저) 를 기반으로 작성되었습니다.



개요


(출처: 네이버 웹툰 <공대생 너무만화>)


  Python은 무시무시한 확장성을 자랑하는 언어이다. 파일을 다루는 방면에서도 아주 강력한 기능을 지원하는데, 바이너리 파일을 열어서 손쉽게 그 내용을 변경하거나 추가할 수 있다. 웹에서 사용하는 다양한 형식의 이미지 파일에 스크립트를 추가하면 강력한 기능을 가진 해킹 도구를 만들 수 있다. 이번 포스팅에서는 비트맵 파일(.bmp) 에 JavaScript를 삽입해서 쿠키를 저장하고 다시 읽어들이는 간단한 코드를 작성하고, 직접 실습해볼 것이다.



이미지 파일 열어보기

  일단 이미지 파일이 있어야 해킹할 거리가 생긴다. 글쓴이는 그림판에서 1x1 픽셀 크기의 작은 점 하나만 있는 이미지 파일을 만들어서 dot.bmp라는 이름으로 저장했다.



  그리고... 방금 생성한 파일을 헥사 에디터에서 열어보자! 이번 포스팅에서는 HxD를 사용했지만, 다른 프로그램을 사용해도 좋다. 무엇을 하건 방금 만든 비트맵 파일의 16진수 값을 확인할 수만 있다면 상관 없다.



  dot.bmp를 열었을 때 결과는 위 사진과 같다. 여기에서 파랗게 표시된 부분을 살펴보자. 파일의 처음 2byte는 파일을 식별하는 데 사용되는 매직 넘버(Magic Number)이다. 예를 들어 처음이 BM으로 시작하면 비트맵 파일이라는 뜻이다. png, jpg, txt, exe 등 확장자가 달라지면 이 2byte에 들어가는 값도 달라진다. 파일의 맨 처음에 이게 어떤 파일 형식인지 알려줌으로써 확장자가 달라져도 컴퓨터 시스템이 해당 파일의 형식을 읽어들일 수 있는 것이다. jpg 파일의 확장자를 bmp, png, gif 등으로 바꾸어도 이미지 뷰어가 제대로 이미지를 불러낼 수 있는 것은 확장자가 임의로 바뀌어도 이 매직 넘버는 바뀌지 않으므로, 시스템이 jpg 파일임을 알 수 있기 때문이다.


  위에 그림에서 볼 수 있는 bmp 파일의 매직 넘버 0x42와 0x4D는 각각 B, M에 대한 ASCII 코드 포인트이다. 매직 넘버 다음으로 오는 4byte는 파일의 크기를 byte 단위로 나타내는 것이다.


  일단 선작업은 이걸로 끝이다! 본격적으로 실습을 위해 코드를 작성해 보자.



이미지 파일에 스크립트 집어넣기

  개요에서 말한 것과 같이 우리는 비트맵 파일에 JavaScript 코드를 삽입할 것이다. 다음은 브라우저가 사용하기 위해 PC에 기록하는 정보인 쿠키를 저장하고, 다시 경고 창으로 출력하는 동작을 하는 스크립트이다.


1
2
3
4
5
6
name = 'id';
value = 'kinew'
var todayDate = new Date();
todayDate.setHours(todayDate.getDate() + 7)
document.cookie = name + "=" + escape(value) + "; path=/; expires=" + todayDate.toGMTString() + "";
alert(document.cookie)
cs


  위 코드를 작성한 후 dot.js 라는 이름으로 저장했다. 1~2행에서는 쿠키에 저장되는 Name과 Value 값을 지정해 주었다. 여기서는 name='id'value='kinew'가 쿠키에 저장된다. 그 다음에는 쿠키의 유효 시간을 7일 (4행) 로 설정해 주었다. 마지막으로 설정된 쿠키를 경고 창으로 보여주는 스크립트를 추가한다.


  이제 dot.js를 dot.bmp에 삽입해보자! 직접 삽입을 실행하는 프로그램은 Python으로 작성하였다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#1
pfile = open("dot.bmp""a+b")
buff = pfile.read()
buff.replace(b'\x2A\x2F', b'\x00\x00')
pfile.close()
 
#2
pfile = open("dot.bmp""w+b")
pfile.write(buff)
pfile.seek(20)
pfile.write(b'\x2F\x2A')
pfile.close()
 
#3
pfile = open("dot.bmp""a+b")
pfile.write(b'\xFF\x2A\x2F\x3D\x31\x3B')
pfile.write(open('hello.js''rb').read())
pfile.close()
cs


  이 코드에서는 3가지 작업을 한다. 각각 #1, #2, #3으로 분류해서 하나하나 살펴보자.


#1 : 오류 제거

  우선 dot.bmp를 바이너리 읽기 전용 모드 (r+b)로 열어서 파일 디스크립터를 pfile이라는 변수에 저장한다. 파일을 읽어들인 결과는 buff에 저장되는데, 4행에 주목해보자. 4행에서는 dot.bmp에 '\x2A\x2F' 값을 '\x00\x00'으로 대체하고 있다. '\x2A\x2F'가 뭐길래? Python 쉘에 간단하게 입력해서 알아보았다.



  '\x2A'는 '*', \x2F'는 '/'. 따라서 '\x2A\x2F''*/'이었다. 이러한 값은 스크립트 실행 중에 오류를 발생시킬 수 있는 값인 모양이다. 그러므로 위 코드에서는 '*'와 '/' 문자를 공백 (\x00\x00)으로 치환해서 무효화시키려는 것이다.


#2 : 주석문 삽입

  두 번째 단락(?)에서는 이번에는 dot.bmp 파일을 쓰기 모드로 열고, buff 변수에 저장된 내용을 dot.bmp에 기록한다. 쓰기 작업을 할 때 매직 넘버를 건드리면 안되므로 11행에서는 읽기 커서를 2byte 뒤로 이동시킨다. 그 다음 12행에서는 매직 넘버 뒤에 주석문의 시작을 의미하는 '\x2F\x2A','/*'를 삽입한다. 브라우저는 매직 넘버반 인식하면 나머지 데이터에 일부 손상이 발생하더라도 비트맵 파일을 정상적으로 읽을 수 있기 때문에 실습에 지장은 전혀 없다.

#3 : 주석문 닫기, 그리고 스크립트 삽입

  세 번째 파일 열기에서는 추가 모드 (a+b)로 연다. 즉 파일의 가장 끝에서부터 write 한다는 의미이다! 아까 #2에서 주석문을 넣어놓았으니, 그 다음에 있는 내용들은 전부 주석 처리가 되었을 것이다. 16행에서 '*/'을 넣어서 주석문을 닫고, 17행에서는 dot.js를 비트맵 파일에 삽입하여 실행할 수 있게 한다.


  다 했으면 이제 Python 코드를 실행시켜 보고 결과를 살펴보자.


실행

  앞에서 짠 Python 스크립트를 실행한 후 dot.bmp를 다시 한 번 헥사 에디터로 열었다.


  비트맵 파일의 크기가 스크립트가 추가되어 증가했다! 이전에 있던 내용이 주석처리되고, 그 다음에 우리가 작성했던 dot.js 파일의 내용이 들어가 있는 것을 볼 수 있다. 이제 dot.bmp에 들어가 있는 스크립트로 js파일을 대체할 수 있다!


  마지막으로 스크립트가 심어진 비트맵 파일을 시랭하는 간단한 HTML 파일을 만들어 보자. dot.bmp의 이미지와 비트맵 이미지에 추가된 스크립트를 실행하는 코드를 각각 만들어 보았다.


1
2
<img src="dot.bmp">
<script src="dot.bmp"></script>
cs


  이제 이 HTML 파일을 인터넷 브라우저에서 열어보자.



  Chrome에서 실행하면 alert가 나오긴 하는데... 우리가 저장한 쿠키 값이 출력되지는 않는다. bmp 파일의 이미지가 출력되지도 않는다... 아마 Chrome에서 자체적으로 이러한 스크립트를 차단하는 조치를 취한 것 같다.



  Internet Explorer에서 HTML 파일을 열면 쿠키가 출력된다.


마무리

  이번 포스팅에서는 간단하게 쿠키를 저장하고 경고 창으로 쿠키를 출력하는 스크립트만 작성하여 이미지 파일에 삽입하고, 그것을 실행해 보았다. 이런 기술을 어디에 활용할 수 있을까?



  PC에 저장된 쿠키를 얻어 특정 사이트로 전송하는 스크립트를 비트맵 파일에 삽입했다고 가정해보자. 사용자들이 많이 접속하는 게시판에 비트맵 파일을 올려놓으면 게시물을 읽은 사용자 쿠키 정보가 해커가 원하는 사이트로 전송될 것이다. 그렇게 된다면 이 정보를 통해 XSS 공격을 유도할 수 있다.