> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/sast-and-sca/ocular/sample-use-cases/attack-surface.md).

# How to Uncover the Attack Surface

Your application's attack surface is the total sum of vulnerabilities that can be exploited to carry out a security attack. The attack surface should be limited in size to protect from unauthorized and malicious users. You can examine your code's attack surface using Ocular. This is frequently the first step in investigating and mitigating vulnerabilities so that you can prioritize strengthening the most vulnerable attack points.

This use case is based on [the HelloShiftLeft sample application](https://github.com/ShiftLeftSecurity/HelloShiftLeft). The focus is an object deserialization vulnerability in the `AdminController`

```
...
@Controller
public class AdminController {
...
@RequestMapping(value = "/admin/login", method = RequestMethod.POST)
public String doPostLogin(
  @CookieValue(value = "auth", defaultValue = "notset") String auth,
  @RequestBody String password, HttpServletResponse response,
  HttpServletRequest request) throws Exception {
...
if (!auth.equals("notset")) {
   if(isAdmin(auth)) {
     request.getSession().setAttribute("auth",auth);
     return succ;
   }
 }
 ...
}
...
private boolean isAdmin(String auth) {
try {
  ByteArrayInputStream bis = new ByteArrayInputStream(
    Base64.getDecoder().decode(auth));
  ObjectInputStream objectInputStream = new ObjectInputStream(bis);
  Object authToken = objectInputStream.readObject();
  return ((AuthToken) authToken).isAdmin();
  } catch (Exception ex) {
    System.out.println(" cookie cannot be deserialized: "
                      +ex.getMessage());
    return false;
  }
}
...
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

In this code fragment, a cookie is received through HTTP and eventually deserialized to create a Java object, an optimistic practice that is often exploited by attackers for arbitrary code execution.

The Code Property Graph (CPG) contains information about the processed code on different levels of abstraction: dependencies, type hierarchies, control flow, data flow, and instruction-level information. The CPG can be queried using Ocular in either [interactive or non-interactive modes](/sast-and-sca/ocular/modes.md).

Using interactive querying, explore the program dependencies by

```
cpg.dependency.name.l
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

A complete list of all dependency names is returned. Since the Code Property Graph Query Language is a Scala-based DSL, it also supports functional combinators. For example, to output (name, version) pairs, use the expression

```
cpg.dependency.map(x => (x.name, x.version)).l
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

which yields

```
List[(String, String)] = List(
  ("zt-exec", "1.9"),
  ("httpclient", "4.3.4"),
  ("lombok", "1.16.6"),
  ("commons-io", "2.5"),
  ("joda-time", "unknown"),
  ("jasypt", "1.9.2"),
  ("jackson-databind", "unknown"),
  ("spring-boot-starter-web", "unknown"),
  ("jasypt-spring-boot-starter", "1.11"),
  ("spring-boot-starter-test", "unknown"),
  ("spring-web", "unknown"),
  ("hsqldb", "unknown"),
  ("jackson-mapper-asl", "1.5.6"),
  ("spring-boot-starter-actuator", "unknown"),
  ("spring-boot-starter-data-jpa", "unknown"),
  ("logback-core", "1.1.9"),
  ("spring-web", "4.3.6.RELEASE"),
  ("tomcat-embed-websocket", "8.5.11"),
  ...
)
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

It is also possible to process CPG subgraphs using external programs by exporting them to JSON. For example, the command

```
cpg.dependency.toJson |> "/tmp/dependencies.json"
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

dumps complete dependency information into the file "/tmp/dependencies.json" in JSON format.

Fields of the CPG can be queried using regular expressions. So, to determine whether an application uses the Spring framework, a quick query is

```
cpg.dependency.name(".*spring.*").l.nonEmpty
=> true
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Since the HelloShiftLeft application uses Spring, it makes sense to look for the typical Java annotations that indicate attacker-controlled variables

```
cpg.annotation.name(".*(CookieValue|PathVariable).*").l
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

From annotations, look at the parameters using

```
cpg.annotation.name(".*(CookieValue|PathVariable).*").parameter.name.l
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

which yields

```
List[String] = List("customerId", "customerId", "customerId", "accountId", "accountId", "accountId", "accountId", "auth", "auth")
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Tracking these attacker-controlled variables shows all data flows originating at them. To do this, first define the set of sinks to be all parameters annotated by CookieValue or PathVariable

```
def sources = cpg.annotation.name(".*(CookieValue|PathVariable).*").parameter
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

then define the set of sinks to be all parameters

```
def sinks = cpg.method.parameter
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Finally, enumerate all flows from sources to sinks

```
sinks.reachableBy(sources).flows.p
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

The flows can be examined manually or automatically. For example, to determine parameters controlled as a result of data flows

```
sinks.reachableBy(sources).flows.sink.parameter.l
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

The query determines those sinks reachable by sources and examines the corresponding data flows. The last flow element is extracted of each flow through the `pathElemens.last` directive, and the corresponding parameter is retrieved. The result of the query can be stored in a variable for further processing, which is useful when determining a large number of data flows

```
def controlled = sinks.reachableBy(sources).flows.sink.parameter.l
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Now retrieve the parameter index ("ast child number" and method full name)

```
controlled.map(x => s"Controlling parameter ${x.order} of ${x.start.method.fullName.l.head}").whereNot(_.contains("<operator>")).sorted
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

yielding

```
  "Controlling parameter 0 of java.io.ObjectInputStream.readObject:java.lang.Object()",
  "Controlling parameter 0 of java.lang.String.equals:boolean(java.lang.Object)",
  "Controlling parameter 1 of io.shiftleft.controller.AdminController.isAdmin:boolean(java.lang.String)",
  "Controlling parameter 1 of io.shiftleft.repository.AccountRepository.findOne:java.lang.Object(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.AccountRepository.findOne:java.lang.Object(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.AccountRepository.findOne:java.lang.Object(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.AccountRepository.findOne:java.lang.Object(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.CustomerRepository.delete:void(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.CustomerRepository.exists:boolean(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.CustomerRepository.exists:boolean(java.io.Serializable)",
  "Controlling parameter 1 of io.shiftleft.repository.CustomerRepository.findOne:java.lang.Object(java.io.Serializable)",
  "Controlling parameter 1 of java.io.ByteArrayInputStream.<init>:void(byte[])",
  "Controlling parameter 1 of java.io.ObjectInputStream.<init>:void(java.io.InputStream)",
  "Controlling parameter 1 of java.lang.Long.valueOf:java.lang.Long(long)",
  "Controlling parameter 1 of java.lang.Long.valueOf:java.lang.Long(long)",
  "Controlling parameter 1 of java.lang.Long.valueOf:java.lang.Long(long)",
  "Controlling parameter 1 of java.lang.Long.valueOf:java.lang.Long(long)",
  "Controlling parameter 1 of java.util.Base64$Decoder.decode:byte[](java.lang.String)",
  "Controlling parameter 2 of javax.servlet.http.HttpSession.setAttribute:void(java.lang.String,java.lang.Object)"
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

In particular, notice that the instance parameter (with an index of 0) of the method `ObjectInputStream.readObject` is controlled, that is, the deserialization vulnerability exists. This shows a more exploratory way of identifying the vulnerability.
