Download as xlsx, pdf, or txt
Download as xlsx, pdf, or txt
You are on page 1of 30

Description

Vulnerable code

Remediation
Secure code
Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise
benign and trusted web sites. XSS attacks occur when an attacker uses a web application to send malicious
code, generally in the form of a browser side script, to a different end user.

Cross-Site Scripting (XSS) attacks occur when:


1. Data enters a Web application through an untrusted source, most frequently a web request.
2. The data is included in dynamic content that is sent to a web user without being validated for malicious
content.

Cross Site Scripting, or XSS comes in three major flavors: Reflected, Stored, and DOM-based. This document
will discuss the first two. It is a vulnerability that arises when a developer chooses to trust user input without
validation.
Reflected XSS
Reflected XSS is very common in web applications. These attacks occur when a client machine sends a
script to a webserver that reflects it back to the client, which executes the script.
Stored XSS
Similar to the Reflected Version, Stored XSS occurs when an attacker can send a script to a web server
that stores the script on the server side, and may serve it back to clients whenever they interact with it.
</pega:otherwise>

</pega:choose>

<%

String toolTip= tools.getParamValue("Tooltip");

if(toolTip.length() > 0){

if(toolTip.startsWith(".")){

toolTip = tools.getActive().getParentPage().getString(toolTip);

strValue = tools.getLocalizedTextForString("pyCaption",toolTip);

%> title='<%=strValue%>'<%

%>

>

<%

strValue = tools.getLocalizedTextForString(tools.getParamValue("FiledValueKey").length() == 0 ?
"pyCaption" : tools.getParamValue("FiledValueKey"),tools.getActiveValue());
%>

<%=strValue%>

</a>

All values that come from a client that are not validated or filtered on input, including Parameters and
Clipboard Properties (Active Values) must be encoded during output processing.

There are several APIs to use for encoding, depending upon the context of the output data (HTML, URL,
RichText, etc)

tools.appendCSF()
StringUtils.crossScriptingFilter()
StringUtils.filterRichText()
StringUtils.URLCrossScriptingFilter()
StringUtils.reversibleCrossScriptingFilter()
escapeCrossScriptingFilter()
escapeIntoJSON()
</pega:otherwise>

</pega:choose>

<%

String toolTip= tools.getParamValue("Tooltip");

if(toolTip.length() > 0){

if(toolTip.startsWith(".")){

toolTip = tools.getActive().getParentPage().getString(toolTip);

strValue = tools.getLocalizedTextForString("pyCaption",toolTip);

strValue = StringUtils.crossScriptingFilter(strValue);
%> title='<%=strValue%>'<%

%>

>

<%

strValue = tools.getLocalizedTextForString(tools.getParamValue("FiledValueKey").length() == 0 ? "pyCaption" :


tools.getParamValue("FiledValueKey"),tools.getActiveValue());
strValue = StringUtils.crossScriptingFilter(strValue);
%>

<%=strValue%>

</a>
Cross Site Scripting (XSS)
Description

Vulnerable code
Remediation

Secure code
A SQLi vulnerability allows a user to send SQL commands to a server and have the underlying database execute
them. This means a user could send DBMS (Database Management System) commands to disconnect the
database, or change passwords. The user could access data from the database that he/she is not authorized to
see, or even change information he/she should not be allowed to change. There are two main flavors of SQLi
though their modelling and remediation are exactly the same:

(Visible) SQL Injection:


SQL commands may be injected or appended to current queries. Queries return information to the page
either as content or as error messages.

Blind SQL Injection


SQL commands may be injected or appended to current queries. Queries do not return information or error
messages (they may alter content behind the scenes).

String loginName = tools.getParamValue("login");


String mysql = "select pyUserName "
+ "from {Class: Data-Admin-Operator-ID} "
+ "where pyUserIdentifier = '"
+ loginName
+ "'";
ClipboardPage pgContentPage = tools.createPage("Code-Pega-List", "myAwesomeResults");
pgContentPage.putString(".pyMaxRecords", "10000");
try{
tools.getDatabase().executeRDB(mysql , pgContentPage);
userName = pgContentPage.getProperty("pxResults").getPageValue(1).getString("PYUSERNAME");

if (userName == null || userName.length() == 0) {


userName = "User not found";
}
}
catch (DatabaseException e){
userName = e.getMessage();
tools.getProperty("myErrorProperty").addMessage(e.getMessage());
}
These are the options in order of most secure.
1. Do not write SQL
Simply put, avoid the situation entirely. Try to use a report definition, any of the Obj- APIs, or any other
mechanic, if possible.

2. Use ExecuteRDB with Literals and Bind Variables


To use a Bind Variable, take any externally set variable or piece of data and enclose it in braces with a
reference to it's type. For example: if (tools.getDatabase().executeRDB("select pyActionTime
as \"pyActionTime\" from {Class:Assign-WorkBasket}
where pxObjClass = 'Assign-WorkBasket' and pyFlowType = 'OverallSLA' and pyActionTime is not null and
pxRefObjectKey = {pyQueryPage.pxRefObjectKey}", lp)

3. PreparedStatement (CoreEngine)
In the engine, it may become necessary to write a SQL query. In this case utilize prepared statements like so:
String selectTableSQL = "select * from myTable where myColumn = ?";
DatabaseConnection connection =
getThreadConnectionStore().getConnection(instanceClassGroup.getDatabase());
DatabasePreparedStatement stmt = connection.prepareStatement(selectTableSQL, instanceClassGroupName,
DatabasePreparedStatementImpl.sSQLSelect, sLookupTable);
stmt.setString(1, "myColumnName");
DatabaseResultSet results = (DatabaseResultSet) stmt.executeQuery();

String loginName = tools.getParamValue("login");


tools.putParamValue(".userName",loginName);
String mysql = "select pyUserName "
+ "from {Class: Data-Admin-Operator-ID} "
+ "where pyUserIdentifier = {pyWorkPage.userName}";
ClipboardPage pgContentPage = tools.createPage("Code-Pega-List", "myAwesomeResults");
pgContentPage.putString(".pyMaxRecords", "10000");
try{
tools.getDatabase().executeRDB(mysql , pgContentPage);
userName = pgContentPage.getProperty("pxResults").getPageValue(1).getString("PYUSERNAME");

if (userName == null || userName.length() == 0) {


userName = "User not found";
}
}
catch (DatabaseException e){
userName = e.getMessage();
tools.getProperty("myErrorProperty").addMessage(e.getMessage());
}
SQL Injection (SQLi)
Description

Vulnerable code

Remediation
Secure code
1. Denial Of Service
This XML snippet would print out "lol" one billion times (Wiki Billion Laughs)

<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

This would take up approximately 3 GB, since "lol" is 3 bytes * 1,000,000,000 ~= 2.79 GB. Longer strings would
take more memory.

2. Additionally one could chain the file inclusion vector with this attack vector, so that a file's contents is
printed one billion times, which would use system I/O time, as well as have huge memory consumption.
<?xml version="1.0"?>
<!DOCTYPE file [
<!ELEMENT file ANY>
<!ELEMENT shad SYSTEM "file:///etc/shadow">
]>

<XXEDemo>
<output>
<file>&shad;</file>
</output>
</XXEDemo>

File incomingFile = new File(incomingFileLocation);


DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(incomingFile);

For each DocumentBuilderFactory created, immediately set the following:


DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

For each SAXParserFactory instance, immediately set the following:


SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);

For each TransformerFactory instance, immediately set the following:


TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
File incomingFile = new File(incomingFileLocation);
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
docBuilder.setExpandEntityReferences(false);
Document doc = docBuilder.parse(incomingFile);
XML External Entity Attack (XXE)
Description

Vulnerable code
Remediation

Secure code
We often do our own deserialization using the capabilities provided by the Java API. Since
serialization/deserialization vulnerabilities are common and some known instances like the Commons-
Collection deserialization have allowed arbitrary code execution, we should be vigilant when performing
deserialization to make sure that we do not have vulnerabilities in our functionality.
{
public static void main(String[] args)
{
try
{
StudentInfo si = new StudentInfo("Abhi", 104, "110044");
FileOutputStream fos = new FileOutputStream("student.ser");
Objectoutputstream oos = new ObjectOutputStream(fos);
oos.writeObject(si);
oos.close();
fos.close();
}
catch (Exception e)
{ e. printStackTrace(); }
}
}

/////////////////////////////////////////////////////////

class DeserializationTest
{
public static void main(String[] args)
{
StudentInfo si=null ;
try
{
FileInputStream fis = new FileInputStream("student.ser");
ObjectOutputStream ois = new ObjectOutputStream(fis);
si = (StudentInfo)ois.readObject();
}
catch (Exception e)
{ e.printStackTrace(); }
System.out.println(si.name);
System.out. println(si.rid);
System.out.println(si.contact);
}
}
If possible, do not deserialize untrusted data without validating the contents of the object stream. In order to
validate classes being deserialized, the look-ahead deserialization pattern should be used.

The object stream will first contain the class description metadata and then the serialized bytes of their
member fields. The Java serialization process allows developers to read the class description and decide
whether to proceed with the deserialization of the object or abort it. In order to do so, it is necessary to
subclass java.io.ObjectInputStream and provide a custom implementation of
theresolveClass(ObjectStreamClass desc) method where class validation and verification should take place.

While the ideal approach in this situation is to whitelist the expected classes, in some scenarios, this approach
may not be practical. A blacklist approach is better for complex object graph structures. Keep in mind that
although some classes to achieve code execution are publicly known, there may be others that are unknown or
undisclosed, so a whitelist approach will always be the preferred approach. To avoid denial of service attacks, it
is recommended that you override the resolveObject(Object obj) method in order to count how many objects
are being deserialized and abort the deserialization when a threshold is surpassed.
public class LookAheadDeserializer {

private static byte[] serialize(Object obj) throws IOException {


ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
byte[] buffer = baos.toByteArray();
oos.close();
baos.close();
return buffer;
}

private static Object deserialize(byte[] buffer) throws IOException,


ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);

// We use LookAheadObjectInputStream instead of InputStream


ObjectInputStream ois = new LookAheadObjectInputStream(bais);

Object obj = ois.readObject();


ois.close();
bais.close();
return obj;
}

public static void main(String[] args) {


try {
// Serialize the Student instance
byte[] serializedStudent = serialize(StudentInfo("Abhi", 104, "110044"));

// Deserialize the Student instance


Bicycle bicycle0 = (StudentInfo) deserialize(serializedStudent);
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
}
Java Deserialization
Description

Vulnerable code
Remediation

Secure code
A common technique in Pega 7 applications is to use conditional visibility and/or privileges on user interface
elements like menu options or sections. This technique is an effective way to have the user interface display
what the user needs to see without displaying what the user does not need to see. In some situations, this
technique accomplishes a part of what is intended but is thought to accomplish all of what is intended. For
example, if there is a menu option that you want admin users to be able to execute, but you do not want
regular users to be able to execute, you can hide the option.
While hiding unusable menu options is important, by itself it does not prevent the functionality underlying the
menu option from being used. In order to prevent the functionality from being used you also have secure the
underlying functionality.

An easy way to replicate an attack for proof of concept or testing purposes is to get a copy of the URL used by
an authorized user and change the parameters in the URL to match that of an unauthorized user and run the
URL from the unauthorized user's session. For example, the 'manger tools' option in the PegaCS application is
run by the admin user with the following URL:
http://10.61.8.217:9080/prweb/g39KfsPA6--ty8Xd36mkqQ%5B%5B*/!TABTHREAD0?pyActivity=
%40baseclass.pzTransformAndRun&pzFromFrame=&pzPrimaryPageName=pyDisplayHarness&pzTransactionId
=&preActivity=&preActivityParams=&action=display&harnessName=CPMManagerToolsHarness&className=C
PM-
Portal&pyDataTransform=&pyPreActivity=doUIAction&pzPrimaryPage=pyDisplayHarness&checkForNewPage=t
rue&portalName=CPMInteractionPortal&contentID=_blank&label=&api=setDocumentLabel&readOnlyMode=f
alse&pzHarnessID=HID3D38FC1816C4F9487696A5FBCF6D38D8

If I look at a URL from an unauthorized user and extract the access group hash (IdC5PtOBqG4%5B*) and
pzHarnessID (HID27A5F3C1B0EEC808332998F6D7EC1B1F)
http://10.61.8.217:9080/prweb/IdC5PtOBqG4%5B*/!TABTHREAD0?
pyActivity=pzRunActionWrapper&pzTransactionId=&pzFromFrame=&pzPrimaryPageName=pyDisplayHarness&
pzDataTransform=CPMSetCurrentLeftNavSelected&inStandardsMode=true&AJAXTrackID=1&pzHarnessID=HID
27A5F3C1B0EEC808332998F6D7EC1B1F&HeaderButtonSectionName=SubSectionCPMDashboard_ChartsBB

and replace the existing the access group hash and pzHarnessID in the first URL with them I get the following:
http://10.61.8.217:9080/prweb/IdC5PtOBqG4%5B*/!TABTHREAD0?pyActivity=
%40baseclass.pzTransformAndRun&pzFromFrame=&pzPrimaryPageName=pyDisplayHarness&pzTr
ansactionId=&preActivity=&preActivityParams=&action=display&harnessName=CPMManagerTools
Harness&className=CPM-
Portal&pyDataTransform=&pyPreActivity=doUIAction&pzPrimaryPage=pyDisplayHarness&checkFo
rNewPage=true&portalName=CPMInteractionPortal&contentID=_blank&label=&api=setDocumentL
abel&readOnlyMode=false&pzHarnessID=HID27A5F3C1B0EEC808332998F6D7EC1B1F
and if I run this URL in the unauthenticated user's session I get the manager tools with the same level of
functionality as the admin user.
The best way to remediate the attack depends on the situation but it is a best practice to have actions that
require authorization check for authorization before every potential execution. There are 3 ways to
accomplish this:

1. Privilege : One way to incorporate checking for authorization before the execution of a rule is to use the
Security Tab of rules to only allow authorized users to invoke those rules. One example of this from Pega 7 is
the Approve Flow Action in @baseclass which can only be invoked by a user that has either the ActionApprove
or the AllFlowsActions privilege for @baseclass.
2. R-U-F : Another way to check for authorization before execution is using built-in functions that check
privileges and/or roles like HaveAuthorization, HavePrivilege, or HaveRole. An example of this can be found in
the pzModifyAllowed Access When rule.
3. Access When : Another way to check for authorization before execution is by using Access When rules.
Access When rules are similar to When rules from the UI perspective, however Access When rules are specific
to checking whether a user should be allowed to perform an operation or to access specific information

Enforce the privilege check on the 'CPMManagerToolsHarness' harness.


Visibility Settings and Privileges in User Interface
Description

Vulnerable code

Remediation

Secure code
What is a secure ID? A secure ID is used as a key that hides important data that you don't want a user knowing
about. This data could include Pega internal information, such as requestor information, or the data could be
personal information of our customers' clients. Generating these IDs in as secure a manner as possible is highly
important, given the data they should hide. This document will detail the ways to generate an ID securely and
also talk about how to rearchitect your solutions to ensure that your IDs betray no data.
Random r=new Random();
int i=r.nextInt();

In its simplest form, IDs should be generated using PRRandom. PRRandom uses java.security.SecureRandom
and reseeds the data frequently, further hampering attackers.

You can get an instance by doing the following in engine code:


private static Random mRandom = PREngineProvider.getExec().getRandom();
Or in rules:
private static Random mRandom =ThreadContainer.get().getSystemRandom();

IDs should be used as keys to hide important data stored server-side. That data should never be sent in any
format to the client.

The only place System Time should be considered is to use it as a timestamp, not for any secure IDs.

SecureRandom sr=new SecureRandom();


sr.next();
Note : Avoid calling setSeed method on the SecureRandom object.
Secure ID Creation

You might also like