моя верблюжья заявка проверяется на безопасность. они указывают на то, что верблюжья электронная почта подвергается злонамеренной атаке на систему, предоставляя неверный вредоносный скрипт в качестве вложения, который можно запустить после удаления вложения из электронной почты.
как верблюжья электронная почта может дезинфицировать вложения?
вот что они говорят.
Это проблема безопасности, потому что любой может запустить код на сервере от имени пользователя root, просто отправив электронное письмо в отслеживаемый почтовый ящик. Сценарий в моем предыдущем сообщении - это то, что я запустил - на своем собственном компьютере - а не на самом сервере, и это приводит к перезаписи /root/.bashrc на сервере - что равняется выполнению команды после запуска любого «законного» пользователя bash от имени пользователя root (поскольку он будет автоматически выполнять команды в /root/.bashrc). Если это имеет больше смысла для вас, это также может быть использовано для изменения любого файла конфигурации на сервере для облегчения дальнейших атак, например, для отправки почты с вложением с именем '../../../../ .. /../../../app/PassThruMultiTenantMT1/latest/etc/users.properties 'можно добавить нового пользователя-администратора для слияния (а затем войти с ним и получить root-доступ).
Вам не нужно вводить значение в процесс создания маршрута, компонент обхода пути является частью "camelFileName" (который извлекается из имен файлов вложений электронной почты, возвращаемых из вызова "getAttachments ( ) ").
Меня меньше интересовало, является ли это проблемой безопасности или нет (это ^^), и больше о том, является ли это проблемой в вашем коде или в коде Apache Camel. И я надеюсь, что вы можете высказать свое мнение по этому поводу.
вот мой код для электронной почты
public class EndpointEmailRouteBuilder extends RouteBuilder {
private static final Logger LOG = LoggerFactory.getLogger(EndpointEmailRouteBuilder.class);
// The route identifier. This identifier is used for monitoring.
private String routeId;
// The configuration of the endpoint.
private String protocol;
private String host;
private String port;
// The account to use.
private String username;
private String password;
// Options when connecting
private String initialDelay;
private String pollDelay;
private String mailDelete;
private String pop3Headers;
private String maxMessagesPerPoll;
private String fetchSize;
// Output destinations
private String toEndpoint;
private String emailArchive;
private String attachmentArchive;
private String errorArchive;
private String destination;
private String predixDestination;
//inbound encryption and compression parameters
private String isCompressedOnly;
private String isEncryptedOnly;
private String isEncryptedWithCompression;
private String pgpKeyUserId;
private String pgpPassword;
private String pgpArmored;
public EndpointEmailRouteBuilder(String routeId,
EndpointEmailDescriptor endpointDescriptor,
String emailArchive,
String attachmentArchive,
String errorArchive,
String destination,
String predixDestination) {
this.routeId = routeId;
this.protocol = endpointDescriptor.getProtocol();
this.host = endpointDescriptor.getHost();
this.port = endpointDescriptor.getPort();
this.username = endpointDescriptor.getUserName();
this.password = endpointDescriptor.getPassword();
this.initialDelay = endpointDescriptor.getInitialDelay();
this.pollDelay = endpointDescriptor.getPollDelay();
this.mailDelete = endpointDescriptor.getMailDelete();
this.pop3Headers = endpointDescriptor.getPop3Headers();
this.maxMessagesPerPoll = endpointDescriptor.getMaxMessagesPerPoll();
this.fetchSize = endpointDescriptor.getFetchSize();
this.emailArchive = emailArchive;
this.attachmentArchive = attachmentArchive;
this.errorArchive = errorArchive;
this.destination = destination;
this.predixDestination = predixDestination;
// Encryption designators
this.isCompressedOnly = endpointDescriptor.getIsCompressedOnly();
this.isEncryptedOnly = endpointDescriptor.getIsEncryptedOnly();
this.isEncryptedWithCompression = endpointDescriptor.getIsEncryptedWithCompression();
this.pgpKeyUserId = endpointDescriptor.getPgpKeyUserId();
this.pgpPassword = endpointDescriptor.getPgpPassword();
this.pgpArmored = endpointDescriptor.getPgpArmored();
}
@Override
public void configure() throws Exception {
if (validateConfiguration()) {
// Format the From Endpoint from Parameters.
final String fromStr = String.format("%s://%s:%s?username=%s&password=%s"
+ "&delete=%s&mail.pop3.forgettopheaders=%s"
+ "&consumer.delay=%s"
+ "&consumer.initialDelay=%s"
+ "&maxMessagesPerPoll=%s"
+ "&fetchSize=%s"
+ "&mapMailMessage=false"
+ "&handleFailedMessage=true"
+ "&skipFailedMessage=true",
protocol, host, port, username, password, mailDelete,
pop3Headers, pollDelay, initialDelay, maxMessagesPerPoll, fetchSize);
final String toStr = String.format("%s", destination);
final String toStrPredix = String.format("%s", pDestination);
onException(com.ab.dig.passthru.inboundemail.exception.EmailProcessorException.class).to("file:" + errorArchive);
onException(java.lang.Exception.class).to("file:" + errorArchive);
//
if (Boolean.parseBoolean(isEncryptedWithCompression)) {
//Compression and Encryption
PGPDataFormat pgpVerifyAndDecrypt = new PGPDataFormat();
pgpVerifyAndDecrypt.setKeyFileName("keys/secring.gpg");
pgpVerifyAndDecrypt.setKeyUserid(pgpKeyUserId);
pgpVerifyAndDecrypt.setPassword(pgpPassword);
pgpVerifyAndDecrypt.setArmored(Boolean.parseBoolean(pgpArmored));
pgpVerifyAndDecrypt.setSignatureKeyFileName("keys/pubring.gpg");
pgpVerifyAndDecrypt.setSignatureKeyUserid(pgpKeyUserId);
pgpVerifyAndDecrypt.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_IGNORE);
from(fromStr).routeId(routeId)
.log(LoggingLevel.INFO, "Message received from " + username + "@" + host)
.setHeader("emailArchive", simple(emailArchive, String.class))
.process(new EmailProcessor()).split().body()
.setHeader("CamelFileName").ognl("request.body.fileName")
.setBody().ognl("request.body.fileContents")
.unmarshal(pgpVerifyAndDecrypt).split(new ZipSplitter())
.streaming().convertBodyTo(String.class)
.wireTap("file:" + attachmentArchive)
.to(toStr)
.to("file:" + toStrPredix);
} else if (Boolean.parseBoolean(isEncryptedOnly)) {
//Encryption Only
PGPDataFormat pgpVerifyAndDecrypt = new PGPDataFormat();
pgpVerifyAndDecrypt.setKeyFileName("keys/secring.gpg");
pgpVerifyAndDecrypt.setKeyUserid(pgpKeyUserId);
pgpVerifyAndDecrypt.setPassword(pgpPassword);
pgpVerifyAndDecrypt.setArmored(Boolean.parseBoolean(pgpArmored));
pgpVerifyAndDecrypt.setSignatureKeyFileName("keys/pubring.gpg");
pgpVerifyAndDecrypt.setSignatureKeyUserid(pgpKeyUserId);
pgpVerifyAndDecrypt.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_IGNORE);
from(fromStr).routeId(routeId)
.log(LoggingLevel.INFO, "Message received from " + username + "@" + host)
.setHeader("emailArchive", simple(emailArchive, String.class))
.process(new EmailProcessor()).split().body()
.setHeader("CamelFileName").ognl("request.body.fileName")
.setBody().ognl("request.body.fileContents")
.unmarshal(pgpVerifyAndDecrypt)
.convertBodyTo(String.class)
.wireTap("file:" + attachmentArchive)
.to(toStr)
.to("file:" + toStrPredix);
} else if (Boolean.parseBoolean(isCompressedOnly)) {
//Only Zipped or Compressed
ZipFileDataFormat zipFile = new ZipFileDataFormat();
zipFile.setUsingIterator(true);
from(fromStr).routeId(routeId)
.log(LoggingLevel.INFO, "Message received from " + username + "@" + host)
.setHeader("emailArchive", simple(emailArchive, String.class))
.process(new EmailProcessor()).split().body()
.setHeader("CamelFileName").ognl("request.body.fileName")
.setBody().ognl("request.body.fileContents")
.unmarshal(zipFile).split(body(Iterator.class))
.streaming().convertBodyTo(String.class)
.wireTap("file:" + attachmentArchive)
.to(toStr)
.to("file:" + toStrPredix);
} else {
//No Compression No Encryption Basic plain data file
from(fromStr).routeId(routeId)
.log(LoggingLevel.INFO, "Message received from " + username + "@" + host)
.setHeader("emailArchive", simple(emailArchive, String.class))
.process(new EmailProcessor()).split().body()
.setHeader("CamelFileName").ognl("request.body.fileName")
.setBody().ognl("request.body.fileContents")
.wireTap("file:" + attachmentArchive)
.to(toStr)
.to("file:" + toStrPredix);
}
} else {
LOG.error("Unable to create route. Invalid Configuration");
}
}
private boolean validateConfiguration() {
boolean returnValue = true;
if (null == username) {
returnValue = false;
} else {
if (username.isEmpty()) {
returnValue = false;
}
}
if (null == password) {
returnValue = false;
} else {
if (password.isEmpty()) {
returnValue = false;
}
}
return returnValue;
}
}
public class EmailProcessor implements Processor {
private static final Logger LOG = Logger.getLogger(EmailProcessor.class);
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
public void process(Exchange exchange) throws Exception {
LOG.info("Entering EmailProcessor...");
Map<String, DataHandler> attachments = exchange.getIn().getAttachments();
if ((null == attachments) || (attachments.size() < 1)) {
throw new EmailProcessorException("Null or 0 attachements");
} else {
LOG.info("attachments.size = " + attachments.size());
}
Map<String, String> emailAttr = gatherEmailHeaderInformation(exchange);
List<String> attachmentFilenames = new ArrayList<String>();
try {
List<FilenameAndContents> attachmentArray = new ArrayList<FilenameAndContents>();
for (String name : attachments.keySet()) {
DataHandler dh = attachments.get(name);
String filename = dh.getName();
//String contents = exchange.getContext().getTypeConverter().convertTo(String.class, dh.getInputStream());
byte[] contents = exchange.getContext().getTypeConverter().convertTo(byte[].class, dh.getInputStream());
LOG.info("Attachment file name: " + filename);
attachmentFilenames.add(filename);
FilenameAndContents attachmentFile = new FilenameAndContents();
attachmentFile.setFileContents(contents);
attachmentFile.setFileName(filename);
attachmentArray.add(attachmentFile);
}
exchange.getIn().setBody(attachmentArray);
} catch (org.apache.camel.TypeConversionException tce) {
throw new EmailProcessorException(
"Unable to type convert from file to string", tce);
} catch (java.io.IOException ioe) {
throw new EmailProcessorException(
"IOException while obtaining Input Stream", ioe);
} catch (java.lang.UnsupportedOperationException uoe) {
throw new EmailProcessorException(
"UnsupportedOperationException add operation is not supported by list",
uoe);
} catch (java.lang.ClassCastException cce) {
throw new EmailProcessorException(
"ClassCastException element prevents it from being added to list",
cce);
} catch (java.lang.NullPointerException npe) {
throw new EmailProcessorException(
"NullPointerException element is null", npe);
} catch (java.lang.IllegalArgumentException iae) {
throw new EmailProcessorException(
"IllegalArgumentException property of element prevents it from being added to list",
iae);
}
archiveEmail(emailAttr, attachmentFilenames, exchange);
LOG.info("Exiting EmailProcessor.");
}
private Map<String, String> gatherEmailHeaderInformation(Exchange exchange) {
final String emailBody = exchange.getIn().getBody(String.class);
final Message mailMessage = exchange.getIn().getBody(javax.mail.Message.class);
Map<String, String> attr = new HashMap<String, String>();
try {
if (null != mailMessage) {
final Address[] fromArray = mailMessage.getFrom();
if (null != fromArray) {
String fromStr = convertAddressListToString(fromArray);
attr.put("from", fromStr);
}
final Address[] toArray = mailMessage
.getRecipients(javax.mail.Message.RecipientType.TO);
if (null != toArray) {
String toStr = convertAddressListToString(fromArray);
attr.put("to", toStr);
}
final Address[] ccArray = mailMessage
.getRecipients(javax.mail.Message.RecipientType.CC);
if (null != ccArray) {
String ccStr = convertAddressListToString(fromArray);
attr.put("CC", ccStr);
}
final Address[] bccArray = mailMessage
.getRecipients(javax.mail.Message.RecipientType.BCC);
if (null != bccArray) {
String bccStr = convertAddressListToString(fromArray);
attr.put("BCC", bccStr);
}
final String subjectStr = mailMessage.getSubject();
if (null != subjectStr) {
attr.put("subject", subjectStr);
}
final Date sentDate = mailMessage.getReceivedDate();
if (null != sentDate) {
attr.put("sentDate", sdf.format(sentDate));
}
final Date receivedDate = mailMessage.getSentDate();
if (null != receivedDate) {
attr.put("receivedDate", sdf.format(receivedDate));
}
if (null != emailBody) {
attr.put("body", emailBody);
}
}
} catch (javax.mail.MessagingException me) {
LOG.error("Unable to gather email header information");
}
return attr;
}
private void archiveEmail(Map<String, String> attr,
List<String> attachmentFilenames, Exchange exchange) {
final String archivePath = exchange.getIn().getHeader("emailArchive", String.class);
Path parentP = Paths.get(archivePath);
Path fileP;
if (null != attr.get("receivedDate")) {
fileP = Paths.get(archivePath, exchange.getExchangeId() + "." + attr.get("receivedDate"));
} else {
fileP = Paths.get(archivePath, exchange.getExchangeId());
}
try {
Files.createDirectories(parentP);
} catch (IOException ioe) {
LOG.error("Unable to create email archive directories");
ioe.printStackTrace();
}
Charset charset = Charset.forName("utf-8");
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(fileP,
charset)) {
if (null != attr.get("from")) {
bufferedWriter.write("From:" + attr.get("from"));
bufferedWriter.newLine();
}
if (null != attr.get("to")) {
bufferedWriter.write("To:" + attr.get("to"));
bufferedWriter.newLine();
}
if (null != attr.get("CC")) {
bufferedWriter.write("CC:" + attr.get("CC"));
bufferedWriter.newLine();
}
if (null != attr.get("BCC")) {
bufferedWriter.write("BCC" + attr.get("BCC"));
bufferedWriter.newLine();
}
if (null != attr.get("subject")) {
bufferedWriter.write("Subject:" + attr.get("subject"));
bufferedWriter.newLine();
}
if (null != attr.get("sentDate")) {
bufferedWriter.write("Sent Date:" + attr.get("sentDate"));
bufferedWriter.newLine();
}
if (null != attr.get("receivedDate")) {
bufferedWriter.write("Received Date:"
+ attr.get("receivedDate"));
bufferedWriter.newLine();
}
if (null != attr.get("body")) {
bufferedWriter.write("Body:" + attr.get("body"));
bufferedWriter.newLine();
}
for (String s : attachmentFilenames) {
bufferedWriter.write("Attachment File:" + s);
bufferedWriter.newLine();
}
} catch (IOException ioe) {
LOG.error("Unable to write email archive");
ioe.printStackTrace();
}
}
private String convertAddressListToString(Address[] adds) {
String returnStr = "";
for (Address adr : adds) {
returnStr = returnStr + adr.toString() + " ";
}
return returnStr;
}
}
public class EndpointEmailProcessor implements Processor {
private static final Logger LOG = LoggerFactory.getLogger(EndpointEmailProcessor.class);
// The configuration of the endpoint.
private String protocol;
private String host;
private String port;
// Options when connecting
private String initialDelay;
private String pollDelay;
private String mailDelete;
private String pop3Headers;
private String maxMessagesPerPoll;
private String fetchSize;
// Output locations
private String destinationEndpoint;
private String destination;
private String predixDestination;
@Override
public void process(Exchange exchange) throws Exception {
LOG.info("Entering EndpointEmailProcessor...");
final String endpointConfigurationStr = exchange.getIn().getBody(String.class);
LOG.info("endpointConfigurationStr: " + endpointConfigurationStr);
final String fileName = (String) exchange.getIn().getHeader("CamelFileName");
LOG.info("filename: " + fileName);
if ((null != endpointConfigurationStr) && (null != fileName)) {
Properties props = new Properties();
props.load(new StringReader(endpointConfigurationStr));
if (validateProperties(props)) {
final String fileNameNoExtension = fileName.substring(0, fileName.lastIndexOf('.'));
LOG.info("fileNameNoExtension: " + fileNameNoExtension);
final String routeIdStr = String.format("passthru.inboundEmail.%s.%sRoute", fileNameNoExtension, protocol);
LOG.info("routeIDStr: " + routeIdStr);
if (props.getProperty("action").equalsIgnoreCase("activate")) {
ServiceStatus routeStatus = exchange.getContext().getRouteStatus(routeIdStr);
if ((null == routeStatus) || (routeStatus.isStopped())) {
exchange.getContext().addRoutes(new EndpointEmailRouteBuilder(routeIdStr,
EndpointDescriptorFactory(props),
props.getProperty("emailArchive"),
props.getProperty("attachmentArchive"),
props.getProperty("errorArchive"),
props.getProperty("destination"),
props.getProperty("predixDestination")));
} else {
LOG.info("Route " + routeIdStr + " already started");
}
} else if (props.getProperty("action").equalsIgnoreCase("deactivate")) {
ServiceStatus routeStatus = exchange.getContext().getRouteStatus(routeIdStr);
if (routeStatus.isStarted()) {
exchange.getContext().stopRoute(routeIdStr);
} else {
LOG.info("Route " + routeIdStr + " already stopped");
}
} else {
LOG.error("Invalid Action in Email Properties");
}
}
} else {
LOG.error("Email Configuration File or File Name is null");
}
LOG.info("Exiting EndpointEmailProcessor.");
}
private EndpointEmailDescriptor EndpointDescriptorFactory(Properties p) {
EndpointEmailDescriptor endpointDescriptor = new EndpointEmailDescriptor();
endpointDescriptor.setProtocol(this.protocol);
endpointDescriptor.setHost(this.host);
endpointDescriptor.setPort(this.port);
endpointDescriptor.setUserName(p.getProperty("username"));
endpointDescriptor.setPassword(p.getProperty("password"));
endpointDescriptor.setInitialDelay(this.initialDelay);
endpointDescriptor.setPollDelay(this.pollDelay);
endpointDescriptor.setMailDelete(this.mailDelete);
endpointDescriptor.setPop3Headers(this.pop3Headers);
endpointDescriptor.setMaxMessagesPerPoll(this.maxMessagesPerPoll);
endpointDescriptor.setFetchSize(this.fetchSize);
// Encryption designators
endpointDescriptor.setIsCompressedOnly(p.getProperty("isCompressedOnly"));
endpointDescriptor.setIsEncryptedOnly(p.getProperty("isEncryptedOnly"));
endpointDescriptor.setIsEncryptedWithCompression(p.getProperty("isEncryptedWithCompression"));
endpointDescriptor.setPgpKeyUserId(p.getProperty("pgpKeyUserId"));
endpointDescriptor.setPgpPassword(p.getProperty("pgpPassword"));
endpointDescriptor.setPgpArmored(p.getProperty("pgpArmored"));
return endpointDescriptor;
}
private boolean validateProperties(Properties p) {
boolean returnValue = true;
if (p.containsKey("username")) {
if (p.getProperty("username").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("password")) {
if (p.getProperty("password").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("emailArchive")) {
if (p.getProperty("emailArchive").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("attachmentArchive")) {
if (p.getProperty("attachmentArchive").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("errorArchive")) {
if (p.getProperty("errorArchive").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("destination")) {
if (p.getProperty("destination").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("predixDestination")) {
if (p.getProperty("predixDestination").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
// Encryption designators
if (p.containsKey("isCompressedOnly")) {
if (p.getProperty("isCompressedOnly").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("isEncryptedOnly")) {
if (p.getProperty("isEncryptedOnly").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("isEncryptedWithCompression")) {
if (p.getProperty("isEncryptedWithCompression").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("pgpKeyUserId")) {
if (p.getProperty("pgpKeyUserId").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("pgpPassword")) {
if (p.getProperty("pgpPassword").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
if (p.containsKey("pgpArmored")) {
if (p.getProperty("pgpArmored").isEmpty()) {
returnValue = false;
}
} else {
returnValue = false;
}
return returnValue;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(String port) {
this.port = port;
}
public void setInitialDelay(String initialDelay) {
this.initialDelay = initialDelay;
}
public void setPollDelay(String pollDelay) {
this.pollDelay = pollDelay;
}
public void setMailDelete(String mailDelete) {
this.mailDelete = mailDelete;
}
public void setPop3Headers(String pop3Headers) {
this.pop3Headers = pop3Headers;
}
public void setMaxMessagesPerPoll(String maxMessagesPerPoll) {
this.maxMessagesPerPoll = maxMessagesPerPoll;
}
public void setFetchSize(String fetchSize) {
this.fetchSize = fetchSize;
}
public void setDestinationEndpoint(String destinationEndpoint) {
this.destinationEndpoint = destinationEndpoint;
}
}