Coordinated Disclosure Timeline
- 2023-04-13: Issue reported to the Jenkins Security team.
- 2023-07-12: Advisory published (no fix available).
Summary
Jenkins MathWorks Polyspace Plugin 1.0.5 and earlier does not restrict a file path in a job parameter, allowing attackers with the Job/Configure permission to exfiltrate arbitrary files from the Jenkins controller by sending them in an email notification.
Product
Jenkins MathWorks Polyspace Plugin
Tested Version
Details
Arbitrary file read in PolyspacePostBuildActions.java
(GHSL-2023-079
)
The MathWorks Polyspace Jenkins plugin implements a post-build action in PolyspacePostBuildActions.java
, which has a fileToAttach
field:
src/main/java/com/mathworks/polyspace/jenkins/PolyspacePostBuildActions.java:72
@DataBoundSetter
public void setFileToAttach(String fileToAttach) {
this.fileToAttach = fileToAttach;
}
This field is used when the action is executed, in the perform
method, to attach a local file to an email notification:
public void perform(Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException
{
if (sendToRecipients && (recipients != null) && !recipients.equals("")) {
String attachSource = "";
String attachName = "";
if ((fileToAttach != null) && !fileToAttach.equals("")) {
attachSource = workspace + File.separator + fileToAttach;
attachName = getAttachName(attachSource);
}
sendMail(recipients, generateMailSubject(mailSubject, "", workspace, build), generateMailBody(mailBody, "", attachName, attachSource, workspace, build), attachSource, attachName);
}
// --snip--
It can be seen that fileToAttach
is concatenated to workspace + File.separator
without validation to build attachSource
, which is then passed to getAttachName
, and both are used in sendMail
and generateMailBody
.
sendMail
creates the email object, attaches the file pointed by attachSource
by creating a File
object (which acts on the Jenkins controller), and sends it:
src/main/java/com/mathworks/polyspace/jenkins/PolyspacePostBuildActions.java:130
public void sendMail( @QueryParameter String sendMailTo,
@QueryParameter String subject,
@QueryParameter String text,
@QueryParameter String attachSource,
@QueryParameter String attachName
) throws IOException
{
try {
String charset = Mailer.descriptor().getCharset();
MimeMessage msg = new MimeMessage(Mailer.descriptor().createSession());
// --snip--
// create the message body, with the text followed by the file to attach if any
Multipart multipart = new MimeMultipart();
if (!attachName.equals("")) {
File file = new File(attachSource);
if (file.length() > 10*1024*1024) {
// size of the attachement is too large
// do not attach, and add a message in the mail body
text += "\n\nSize of the attached file is too large - Not attached\n";
} else {
MimeBodyPart attachmentBodyPart= new MimeBodyPart();
DataSource source = new FileDataSource(attachSource);
attachmentBodyPart.setDataHandler(new DataHandler(source));
attachmentBodyPart.setFileName(attachName);
multipart.addBodyPart(attachmentBodyPart);
}
}
MimeBodyPart textBodyPart = new MimeBodyPart();
textBodyPart.setText(text, charset);
multipart.addBodyPart(textBodyPart);
msg.setContent(multipart);
Transport.send(msg);
}
// --snip--
}
There’s one caveat: note that getAttachName
requires the file that is going to be attached to exist in the Jenkins controller (since it uses the File
class directly), but attachSource
is prefixed with the workspace
path, which comes from the agent, so unless the job is running on the controller or the workspace
path also exists in the controller, getAttachName
returns an empty string, which prevents the file from being attached to the email:
src/main/java/com/mathworks/polyspace/jenkins/PolyspacePostBuildActions.java:119
private String getAttachName(String attachSource)
{
if ((attachSource != null) && !attachSource.equals("")) {
File f = new File(attachSource);
if (f.exists() && !f.isDirectory()) {
return f.getName();
}
}
return "";
}
This also prevents the attach functionality from working properly in a distributed Jenkins environment.
This issue was found by the Uncontrolled data used in path expression CodeQL query.
Impact
This issue may lead to arbitrary file read.
Proof of concept
To exploit this vulnerability, the global configuration for the Polyspace plugin needs to have been set up (it doesn’t need to be correct), and a functional SMTP server needs to have been configured in Jenkins.
Then, an attacker with Job/Configure permissions could create a new job, set the Polyspace environment, add a Polyspace Notification post-build step, and configure the Attachment filename property as something like ../../../../../../../../etc/passwd
.
As a proof of concept, Jenkins can be configured to use a local SMTP server at localhost:25
, and the following command can be used to run one locally: sudo python3 -m smtpd -n -d localhost:25
.
After that, when the job is executed, the SMTP server will receive an email containing the desired file (in this case /etc/passwd
) as an attachment. In a real attack, since the attacker controls the recipients, they can just use an email they control, and the configured SMTP server will send the email there.
CVE
- CVE-2023-37960
Resources
Credit
This issue was discovered and reported by the GitHub CodeQL team member @atorralba (Tony Torralba).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-079
in any communication regarding this issue.