wolfman 로그인 후 파일을 확인한다.
코드 파일을 확인한다.
코드 시작 전 힌트를 보면 level 5 [orc] 문제에서 "check length of argv[1]"가 추가되었다.
추가된 코드를 분석해보면 "argv[1]"의 길이가 48보다 크면 문자열을 출력하고 프로그램을 종료시킨다.
이전 문제들을 잘 풀었다면 추가된 if 문에서 막히지 않을 것이다.
왜냐하면, 지금까지 두 번째 if 문에서 "agrv[1][47]"에서 '\xbf'가 들어가고 끝나므로 즉, 47byte에서 끝나고 48byte까지 가지 않아서 생각할 필요가 없어지고 이전 문제들과 같아진다.
gdb로 메모리 분석한다.
이때, 추가된 코드와 메모리 스택만 하겠다.
strlen 함수를 부르고 cmp로 비교한다. JBE(JLE와 비슷) JLE는 "a <= b"다. 즉, a가 b보다 작거나 같으면 main+224로 점프 하라는 것인데 C언어 코드는 "a > b"로 나와 있다. C언어 코드를 보면 "if(strlen(argv[1]) > 48)"인데 48 이하면 거짓이 되므로 if 문을 실행하지 않는다. 이때 어셈블리 코드를 C언어 코드로 바꿔보면 "if(strlen(argv[1]) <= 48)"이 되는데 47 이하면 참이므로 if 문을 실행하지 않고 main+224로 넘어가라 것이다.
C언어에서는 if 문은 참이면 코드 순서대로 실행하지만, 어셈블리 JMP(점프) 코드는 참이면 점프 거짓이면 점프하지 않고 코드 순서대로 이동한다.
간단히 말해서 이중 부정이 긍정을 말하듯이 어떻게 쓰느냐에 따라 달라지는 것이지 결국은 같은 말이다.
어셈블리 언어에서는 if 문이 없다. if 문을 좀 더 저차원 레벨에서 구현해야 한다. if 문을 구현하기 위해서는 CMP와 JMP 계열 명령어를 이해해야 한다.
CMP dst, src
- dst를 중심으로 src와 비교 연산을 수행한다.
- 연산 결과는 플래그 레지스터에 저장된다.
- 결과는 JMP 명령문과 연결되어 사용된다.
JMP 계열
- JE(Jump equal) / JNE(Jump Not Equal) Label : 같으면 / 같지 않다면 Label로 jump (dst == src / dst != src)
- JG(Jump Greater) / JGE(Jump Greater or Equal) Label : 크면 / 크거나 같으면 Lable로 jump (dst > src / dst >= src)
- JL(Jump Less) / JLE(Jump Less or Equal) Label : 작으면 / 작거나 같으면 Lable로 jump (dst < src / dst <= src)
- JMP Label : Label로 무조건 jmp
JMP 계열의 Label은 주소가 들어가는 것이다. 그래서 jmp main+224가 되면 main+224 주소로 점프하라는 뜻이 된다.
어셈블리어 언어는 로직을 구현하는 명령어가 저차원인 이유로 코딩하는 프로그래머의 취향에 따라 같은 로직을 다양한 표현법으로 표현이 가능한 자유도가 아주 아주 큰 언어다. 그래서 if 문을 표현하는 방법이 여러 개가 될 수 있다.
위 접근법은 로직 부분 "if(strlen(argv[1]) > 48)"을 대우 명제 "if(strlen(argv[1]) <= 48)"을 취하여 처리하는 접근법이다.
위치가 변경되면서 참 거짓 블록의 위치가 C언어와 비슷해지는 특징이 있으며, 실행해야 하는 명령어 수가 증가하지 않아서 많은 프로그래머가 이 방법을 선호한다고 한다.
strcpy 함수에 들어가는 주소가 "ebp-40"이고 메모리 스택을 도식화한다.
int i 4byte |
char buffer[40] 40byte |
SFP 4byte |
RET 4byte |
level 5와 같은 메모리 스택을 가지고 있다.
level 5와 같은 메모리 스택을 가지고 있으므로 같은 방법으로 페이로드를 만든다.
main에 break를 걸고 인수를 두 개 주고 실행하고 esp를 찾는다.
argv[2]가 "0xbffffc48"에서 시작한다. argv[2]에 셸 코드를 넣고 argv[2]의 주소를 RET에 넘겨준다.
이 문제를 풀면서 새롭게 배운 게 있다.
gdb에서 break를 걸고 실행시킬 때 인수에 따라 주소가 달라진다는 것이다.
level 5 실패 사례에서 나왔던 Illegal instruction 오류가 실행시킬 때 인수에 따라 나오는 것이었다.
인수가 적으면 더 높은 주소에 "B"와 "A"가 들어가서 우리가 원하는 주소에 도달하지 못해 "segmentation fault"가 일어난다.
gdb에서 실행할 때는 적당히 많은 인수를 넣고 실행시켜야 한다. 그러면 적어도 argv[2]의 주소를 넘지 않고 정확한 주소는 아니라서 "Illegal instruction" 오류가 나는데 이때는 level 5에서 말했듯이 셸 코드 앞에 "\x90"을 넣어서 주솟값을 보정하는 방식으로 해결할 수 있다.
"Illegal instruction" 오류가 났다는 것은 근처에 정확한 주소가 있다는 느낌인 것 같다.
사실 처음에 페이로드를 작성했을 때 "Illegal instruction" 오류가 났다. 그래서 주소를 4byte씩 앞뒤로 보내면서 작성해보니 "\x90" 없이도 실행되는 주소를 찾을 수 있었다.
이 문제의 argv[2]의 주소는 정확히 "0xbffffc48"이다. "Illegal instruction" 오류가 일어나지 않고 바로 셸이 실행됐기 때문이다.
'System Hacking > 해커스쿨 L.O.B Hacking Zone' 카테고리의 다른 글
해커스쿨 L.O.B level 7 [darkelf] (0) | 2020.05.28 |
---|---|
해커스쿨 L.O.B level 5 [orc] (0) | 2020.05.21 |
해커스쿨 L.O.B level 4 [goblin] (0) | 2020.05.18 |
해커스쿨 L.O.B level 3 [cobolt] (0) | 2020.05.17 |
해커스쿨 L.O.B level 2 [gremlin] (0) | 2020.05.16 |
댓글