취약점을 쉽게 발견했지만 RSA 관련해 이해해야할 부분이 있었고
그 부분에 있어서 시간이 좀 걸렸다.
RSA 암호기법은 정보보안기사 공부할 때도 했고, 암호 공부할 때도 했었는데
실제로 암호화 과정, 복호화 과정을 대강 이렇다~ 하고 넘어갔기 때문에
문제를 풀 때 다시 공부를 해야했다.
이 문제를 풀면서 암호화, 복호화 과정을 철저하게 되집은 점이 가장 기분 좋았다.
먼저, 문제 프로그램의 보호기법을 확인하였다.
(보호기법)
이유는 다른 대회문제나 다른 사이트의 포너블같은 경우
아주 당연하게 PIE, NX 기본적인 보호기법이 설정 되어있는데
pwnable.kr의 루키 문제라서 그런지 보호기법이 거의 없었다. 여기서는 카나리 하나 적용되었다.
(그리고 바이너리파일에 C코드 심볼이 첨부되어있어서 읽기도 편했다.)
바이너리 파일을 직접 핸드레이를 하고 전체적인 과정을 파악해 보았다.
크게 첫번째로 RSA 암호화의 p,q e, d를 설정하는 과정이 있다.
그리고 암호화를 위해 선택지2번을 입력하면 아래와 같이 나온다.
(암호화)
그리고 데이터를 입력받는다.
여기서 조금 특이한 과정이 있었는데, 그 이유는 모르겠다.
의도적으로 이상하게 만든건지 이유가 있어서 그런건지.
특이했던건
입력을 fread로 받는데, 1바이트씩 받는다.
그래서 스택의 어느 변수에 하나씩 받아 온 후, 한 자씩 어느 글로벌 배열변수에 저장한다.
(평문 버퍼)
그리고 이 버퍼에서 한 자씩 다시 가져와서 암호화 과정을 걸 친 후 글로벌 배열변수(암호화버퍼)에 저장한다.
(암호문 버퍼)
그리고 이 버퍼에 있는 것을 다시 한자씩 출력용으로 데이터를 가져와서 스택에 저장한 후
그 스택에 있는 것을 출력한다.
위의 과정을 걸쳐 암호화가 진행된다.
여기서 취약한 점은 바로 버퍼 크기이다.
평문 버퍼와 암호문 버퍼 같은 사이즈로 설정해놓았다. 그리고는 평문에서 한자씩 가져와서 RSA 과정을 걸쳐서 암호문 버퍼에 저장한다.
그런데 평문버퍼의 1바이트를 가져와서 RSA 암호화를 거치면 4바이트가 되는데 이 것을 암호문 버퍼에 저장을 하기 때문에 암호문 버퍼는 당연히 넘치게 된다.
(overflow)
암호문 버퍼에 있는 것을 스택으로 옮기는 과정에서도 마찬가지다. 그렇기에 스택도 overflow 낼 수 있다. 하지만 스택 카나리가 있기 때문에 메모리 leak할 방법을 찾아내어야한다.
그렇기에 나는 암호문 버퍼를 overflow하여 func 배열변수를 덮어쓸 생각을 했다.
func 배열 변수에는 함수 주소들이 있다. func[0] 에는 set_key 함수 주소가 들어있었는데,
이 주소를 덮어쓴 후, set key를 실행시키면 우리가 덮어쓴 주소가 시작되게 할 것이다.
나는 평문버퍼 앞부분에 쉘코드를 넣고, func[0] 에다가 평문버퍼의 주소를 덮어쓸 생각을 했다.
왜 자신있게 했느냐 하면, NX가 설정되어있지 않기 때문에!! 이럴때 쉘코드를 마구마구 맘껏 써야겠다는 생각이 들었다.
그렇다면 이제 문제는 어떻게하여 평문버퍼의 주소 0x602560으로 설정할 것인가 였다.
(*평문버퍼의 주소를 확정할 수 있는 이유는 바로 PIE가 설정되어있지 않기 때문에 고정적이기 때문이다.)
한 글자, 즉 0~255의 숫자를 RSA하여 0x602560으로 만들어야한다.
여기까지는 금방 파악했으나 평문버퍼의 주소를 만들기 위해 시간이 꾀 걸렸다.
RSA 암호화 과정
pow(m,e) % N = c
m은 평문이고 c는 암호문이다.
우리는 c가 0x602560이기만 하면 된다.
이것저것 시도하다가 생각이 트인 때가 있었다.
(p,q 설정과정)
실제 RSA 에서는 P와 Q는 굉장히 큰 소수여야한다.
소수!!
하지만 이 프로그램에서는 소수인지 검사하지 않는다. (아마 그렇게 되면 프로그램이 무거워져서 그랬나?)
무튼, 여기 프로그램에서는 p와 q가 소수가 아니어도 된다는 점!
그렇기에 값을 마구마구 설정해서 원하는 c(암호문)값만 만들 수 있으면 된다는 거였다.
pow(m, e) % N = 0x602560
10진수로 쓰면
pow(m, e) % N = 6301024 이고
다시 정리하면
N * a + 6301024 = pow(m, e)
여기서 m은 14, e는 7이라고 설정했다.
그래야 6301024의 근처 숫자가 되기 때문이다.
그렇게 되면
N * a = 99112480
이 되고, N은 6301024 보다 큰 조건을 만족하고, 작은 N의 값을 12389060 으로 설정했다.
그러면 p,q 값을 찾아야하는데
이 값은 잘 정해야한다. 이유는 p,q값이 char로 설정되어있기 때문에 0x0000~부터 0x7FFF 까지의 숫자여야한다.(64비트이기 때문)
N이 12389060 이기 때문에 p,q를 1217, 10180으로 설정했다.
그리고 우리가 원하는 평문버퍼 주소(0x602560)으로 나오는지 확인해보았다.
(확인)
(확인 결과)
딱 나왔다!
이제 끝났다!
func[0]부분까지만 덮어쓰려면 265만큼 넣으면 된다.
(265 바이트 입력)
(결과모습)
이제
페이로드 앞부분에는 쉘코드를 올리고
나머지 241바이트
그 후 0x0e 값을 보낼 것이다.
마지막 바이트가 RSA 과정을 거쳐 평문버퍼의 주소가 된다.
exploit을 작성했다.
(쉘코드 및 변수 설정)
(exploit)
(exploit 결과)
문제 풀이 끝
'WarGame > 500 Project' 카테고리의 다른 글
(67/500) pwnable.kr - note (0) | 2018.03.29 |
---|---|
(65/500) pwnable.kr - echo2 (0) | 2018.03.08 |
(64/500) backdoor - Enter the matrix (0) | 2017.10.31 |
(63/500) pwnable.kr - syscall (2) | 2017.10.22 |
(62/500) NOE.systems - BURYBURY (0) | 2017.10.01 |