No właśnie ,sprawa na pozór błacha ,ale jak się okazuje jednak nie do końca.
Ostatnio w pewnym małym projekcie zachciało mi się manipulować zasada z polisy lokalnej (OS == Windows XP) ,a mianowicie :
Ustawienia zabezpieczeń lokalnych->Zasady konta->Zasady haseł->Hasło musi spełniać wymagania co do złożoności
Jeżeli ktoś jeszcze dziwi się dlaczego w ogóle poruszam ten temat to już wyjaśniam ,że jak najbardziej nie chodzi o ustawienie tej zasady w sposób „klikany” ;), tylko software’owy.
Idąc po najmniejsze lini oporu zacząłem od zapytania googli o klucz/wartość w rejestrze systemowym odpowiedzialny/ą za powyższa zasadę. Niestety nic konkretnego nie udało mi się ustalić ;/. Hymm , no trudno , pomyślałem i już pełen nadziei i optymizmu zacząłem szperać w sieci pod kątem WinApi , które dostarczyło by mi możliwości odczytywania jak i modyfikacji bieżącego stanu zasad polis lokalnych. Szukam, szukam i nic!!!o_O.
Trochę nie chciałem na początku dowierzać ,no ale po chwili do mnie dotarło ,że jednak błyskawicznego rozwiązania na swój „problem” nie znajdę;/.
Oczywiście jak mógłbym się poddać wcześniej nie doprowadzając sprawy do końca;).
Niestety żadna defaultowa windows’owa aplikacja nie przychodziła mi do głowy (prócz mmc.exe + przystawka secpol.msc,,,ale było by za dużo zabawy :P, a jakiej? o tym za chwilę),która pozwała by na modyfikacje polis, ale jako ,że w przeszłości zajmowałem się dobrą chwilę platformą serwerową Windows 2003 do głowy przyszedł mi Resorce Kit Tools.
No i bingo!!! Znajdujemy tam aplikację PASSPROP ,która jak najbardziej spełnia stawiane przeze mnie wymagania:
Passprop.exe /?
Displays or modifies domain policies for password complexity and
administrator lockout.
PASSPROP [/complex] [/simple] [/adminlockout] [/noadminlockout]
/complex Force passwords to be complex, requiring passwords
to be a mix of upper and lowercase letters and
numbers or symbols.
/simple Allow passwords to be simple.
/adminlockout Allow the Administrator account to be locked out.
The Administrator account can still log on
interactively on domain controllers.
/noadminlockout Don't allow the administrator account to be locked out.
Jako ,żę aplikacja ta jest „opensource”(no co ?tak mówi OllyDbg:P), to rzućmy okiem na kod i sprawdźmy wywołania jakich WinApi pojawiają się przy użyciu przełącznika
„/complex”.
Lista wygląda następująco:
ADVAPI32.LsaOpenPolicy
ADVAPI32.LsaQueryInformationPolicy
ADVAPI32.LsaClose
SAMLIB.SamConnect
SAMLIB.SamOpenDomain
SAMLIB.SamQueryInformationDomain
SAMLIB.SamSetInformationDomain
SAMLIB.SamCloseHandle
SAMLIB.SamFreeMemory
Ahh, czyli jednak istnieją api do modyfikacji polis,ajjj Icewall,, Icewall,, nie umiesz szukać :P.Odetchnąłem z ulgą ,że mam już swoje upragnione api i natentychmiast udałem się na strone MSDN ,żeby zbadać ich detale. Bez większego problemu można znaleźć dokumentacje dotyczącą trzech pierwszych api ale dla wszystkich importowanych z SAMLIB już nie;/:
No Results Found For: SamOpenDomain.
Dziwne prawda? Nie wiem jakie były intencje MS przy podjęciu decyzji nie udokumentowania tych api ,no ale cóż .
Po moim małym rozczarowaniu na MSDN ,wrzuciłem „SamOpenDomain” w google, ale niestety i google tym razem było mało pomocne. Jedynym kawałkiem kodu wykorzystującym niektóre z powyższych api jaki znalazłem był kod PwDump’a 2.Nie bawiąc się w dalsze poszukiwania brakujących wskazówek czy wrapper’ów na te api (możliwe ,że NETAPI32 dostarcza podobną funkcjonalność) przystąpiłem do tego co tygrysy lubią najbardziej;).
RE Passprop.exe.
Skupie się jedynie na api , których nie wykorzystuje PwDump2,czyli :
SAMLIB.SamQueryInformationDomain
SAMLIB.SamSetInformationDomain
Jak widać na powyższym screen’e SamQueryInformationDomain
przyjmuje 3 parametry:
SamQueryInformationDomain(HANDLE hDomain,DWORD kind, SamInfoStruct**);
,gdzie z dalszych moich obserwacji wynika ,że struktura SamInfoStruct ,najprawdopodobnie wygląda tak:
struct SamInfoStruct
{
DWORD unknow;
DWORD flag;
};
Interesująca nas tutaj polem jest flag:
„Hasło musi spełniać wymagania co do złożoności”
0 – off
1 – on
No to już mamy procke do ustalenia bieżącej wartości tej zasady teraz jeszcze modyfikacja:
Jak można było się domyśleć SamSetInformationDomain przedstawia się podobnie:
SamSetInformationDomain (HANDLE hDomain,DWORD dunno,SamInfoStruct*);
No i na koniec mały kod wyłaczający/włączający zasade złożoności hasła w lokalnej polisie.
(kod jest „troche” nie chlujny na szybkiego wyciąłem go z projektu i wprowadziłem drobne modyfikacje:P)
———————————–CUT———————————————–
#include
#include
#include
//---------------------------------------------------------------------------
struct SamInfoStruct
{
DWORD unknow;
DWORD flag;
};
typedef NTSTATUS
(WINAPI *SamConnect_t) (DWORD, HANDLE*, DWORD, LSA_OBJECT_ATTRIBUTES*);
typedef NTSTATUS
(WINAPI *SamOpenDomain_t) (HANDLE,DWORD,PSID,HANDLE*);
typedef NTSTATUS
(WINAPI *SamQueryInformationDomain_t)(HANDLE,DWORD,SamInfoStruct**);
typedef NTSTATUS
(WINAPI *SamSetInformationDomain_t)(HANDLE,DWORD,SamInfoStruct*);
typedef NTSTATUS
(WINAPI *SamCloseHandle_t) (HANDLE);
typedef NTSTATUS
(WINAPI *SamFreeMemory_t) (SamInfoStruct*);
using namespace std;
bool ComplexPassword(string inOperationType,bool inSet);
int main(int argc, char** argv)
{
bool flag;
if(argc<2)
return 0;
switch(argv[1][0])
{
case '0':
flag = false;
break;
case '1':
flag = true;
break;
default:
return 0;
}
cout<<"Haslo musi spelniac wymagania co do zlozonosci: "
<<ComplexPassword("check",0)
<<endl;
//ustawienie flagi przy sprawdzaniu nie ma znaczenia
//set flag
ComplexPassword("set",flag);
cout<<"Haslo musi spelniac wymagania co do zlozonosci: "
<<ComplexPassword("check",0)
<<endl;
return 0;
}
bool ComplexPassword(string inOperationType,bool inSet)
{
LSA_HANDLE hPolicy = NULL;
LSA_OBJECT_ATTRIBUTES objAttrib;
POLICY_ACCOUNT_DOMAIN_INFO* pDomainInfo;
SamInfoStruct *SamInfo = NULL;
HANDLE hSam = 0;
HANDLE hDomain = 0;
SamConnect_t SamConnect;
SamOpenDomain_t SamOpenDomain;
SamQueryInformationDomain_t SamQueryInformationDomain;
SamSetInformationDomain_t SamSetInformationDomain;
SamCloseHandle_t SamCloseHandle;
SamFreeMemory_t SamFreeMemory;
HMODULE hLib = LoadLibrary("samlib.dll");
SamConnect = (SamConnect_t)
GetProcAddress(hLib,"SamConnect");
SamOpenDomain = (SamOpenDomain_t)
GetProcAddress(hLib,"SamOpenDomain");
SamQueryInformationDomain = (SamQueryInformationDomain_t)
GetProcAddress(hLib,"SamQueryInformationDomain");
SamSetInformationDomain = (SamSetInformationDomain_t)
GetProcAddress(hLib,"SamSetInformationDomain");
SamCloseHandle = (SamCloseHandle_t)
GetProcAddress(hLib,"SamCloseHandle");
SamFreeMemory = (SamFreeMemory_t)
GetProcAddress(hLib,"SamFreeMemory");
//tak tak tak ...zakładam ,że lib'a i wszystkie adresy usdało sie załadować pomyślnie
memset(&objAttrib,0,sizeof(objAttrib));
objAttrib.Length = sizeof(objAttrib);
if(LsaOpenPolicy(NULL,&objAttrib,POLICY_ALL_ACCESS,&hPolicy)!= 0)
{
cout<<"Password complexity check error: "<<GetLastError();
return false;
}
LsaQueryInformationPolicy(hPolicy,
PolicyAccountDomainInformation,
(void**)&pDomainInfo);
LsaClose(hPolicy);
if(SamConnect(0, &hSam, 0x20,&objAttrib)<0)
{
cout<<"SamConnect error: "<DomainSid,&hDomain)<0)
{
cout<<"SamOpenDomain error: "<<GetLastError();
goto bad_boy;
}
if(SamQueryInformationDomain(hDomain,0x1,&SamInfo)<0)
{
cout<<"SamQueryInformationDomain error: "<flag != 0x1)
goto bad_boy;
inSet = true;
}
else
{
SamInfo->flag = (DWORD)inSet;
if(SamSetInformationDomain(hDomain,0x1,SamInfo)<0)
{
cout<<"SamSetInformationDomain error: "<<GetLastError();
goto bad_boy;
}
}
SamCloseHandle(hDomain);
SamCloseHandle(hSam);
if(SamInfo!=0)
SamFreeMemory(SamInfo);
FreeLibrary(hLib);
return inSet;
bad_boy:
if(hDomain !=0)
SamCloseHandle(hDomain);
SamCloseHandle(hSam);
if(SamInfo!=0)
SamFreeMemory(SamInfo);
FreeLibrary(hLib);
return false;
}
———————————–CUT———————————————–
Przykładowa sesja:
pass.exe 1
Haslo musi spelniac wymagania co do zlozonosci: 0
Haslo musi spelniac wymagania co do zlozonosci: 1
I to by było na tyle:).
Jeżeli ktoś spotkał się z dokumentacją api z SAMLIB lub wrraperami z innego lib’a to będe wdzięczny za link.