우선 위 문제는 총 5가지 메뉴가 존재한다.
채널을 만드는 '/nc'
만들어진 채널에 접속하는 '/jc x'
채널 리스트를 보여주는 '/lc'
종료하는 '/q'
현재 실행시킨것과 같이 가능한 입력을 보여주는 '/h'
또, /nc로 채널을 만들고도, 4가지 메뉴가 있다.
크기를 받아 데이터를 저장하는 '/e'
잠시 채널을 떠나는 '/pc'
채널을 닫고 떠나는 '/qc'
현재 실행시킨것과 같이 가능한 입력을 보여주는 '/h'
이 문제는 처음 만나본 다중 쓰레드 문제여서 조금 난항을 겪었다.
우선 찾은 취약점은 다름아닌 fgets에서 였는데, fgets를 하는 위치가 내가 입력한 값에 따라 현재 주소보다 더 작은 주소에는 마음대로 입력이 가능하다는 것이었다.
더 작은 주소에 마음대로 입력이 가능했기 때문에, 생각을 한 시나리오는 다음과 같았다.
<STACK>
2번 채널
____________________________________
1번 채널
____________________________________
위 처럼, 1번 채널을 만들어 주고, 2번 채널을 만들어 둔 후, 1번 채널에 다시 접속하면, 2번 채널이 1번 채널보다 더 작은 스택에 저장될 것이라고 추측하였다. 그래서 1번 채널의 fgets를 통해 2번 채널의 특정 정보를 변환하면 되지 않을까라고 생각하였다. 하지만 도대체 어느 주소에 무엇을 써야할지가 의문이었다.
Google에 'gdb 멀티쓰레드 디버깅'에 대해 검색해보니 여러 명령어가 나와서 한번 따라해보았다.
여기서 깨달은 것이, 각각의 쓰레드들이 따로 활동중이며, 심지어 내가 '/nc'로 만든 쓰레드들은 모두
__kernel_vsyscall()에서 멈춰있다는 것이었다.
도대체 이 주소가 무엇일지 보았는데, 위와 같은 가젯들이 있었다.
심지어, thread 명령어로 그 쓰레드로 옮겨가서 각각의 스택을 확인해보니,
신기하게도 각 쓰레드에서 사용하는 스택또한 위치가 달랐다. 이제 시나리오를 짤 수 있는 아이디어가 떠올랐다.
<시나리오>
1번 쓰레드 생성 ⇒ 2번 쓰레드 생성 ⇒ 1번 쓰레드 접속하여, 2번 쓰레드의 스택에 __kernel_vsyscall을 참고하여
rop payload 작성 ⇒ 쉘
__kernel_vsyscall에서 edx와 ecx를 pop해주기 때문에, 우리는 eax , ebx만 조정해주고 int 0x80을 하면
rop에 성공할 수 있다.
우선 가젯들을 모두 찾았다.
execve( &("/bin/sh") , &(*"/bin/sh\x00"), &(NULL))
이 형태로 execve를 호출하여 쉘을 딸 것이다.
우리는 execve의 첫번째 인자와 두번째 인자가 필요한데, atoi에서 취약점이 떠올랐다.
atoi는 숫자이후에, 숫자로 인식되지 않는 것이 있으면 그냥 그 전까지 읽어들은 정수만 반환한다.
command에 " 원하는 숫자 + NULL +내가 원하는 값들 "을 저장하게 되면,
command는 bss영역이어서 bss영역에 원하는 정보들 모두를 담을 수 있고, execve의 모든 인자들을 완성할 수 있다.
그렇게 해서, 쉘을 딸 수 있다.
'CTF > CTF_writeup' 카테고리의 다른 글
[zer0pts] diysig (0) | 2020.03.12 |
---|---|
[zer0pts] ROR (0) | 2020.03.12 |
[CODEGATE 2020 예선] Halffeed (0) | 2020.02.09 |
[justCTF 2019] ATM service (0) | 2020.02.09 |
[justCTF 2019] Shellcode Executor PRO Write-up (0) | 2020.02.09 |