Wut to learn from 1pwnch Bakery

Having a chance to create a challenge for PatriotCTF is very exciting. Besides, I really learn something through this experience (about 1pwnch’s Bakery). Therefore, this post will also focus on the writeup of it.

Code review

I think maybe people just don’t want to review the source code, so I will take everyone into the detail of it.
For the first part:

var cache = (duration) => {
  return (req, res, next) => {
    let key = req.hostname
    let cachedBody = mcache.get(key)
    if (cachedBody) {
      res.send(cachedBody)
      return
    } else {
      ...
    }
  }

This is how the server-side cache written in node.js working. The key point is the key=req.hostname, which means that server will identify on hostname to decide whether to respond with cache or new response.
For the second part:

app.post('/upload', function(req, res){
...
})

This part is easier to understand, it can help you upload your file to the server in path of __dirname + '/public/' + b.toString('base64') + '/view/'
For the third part:

app.get('/', cache(0.1), (req, res) => {
  // static ip
  var bread = 'flag{Go Guess it!!}'
  if(req.ip){
        var buffer = new Buffer(req.ip)
        var route = __dirname + '/public/' + buffer.toString('base64') + '/view'
	mkdirp(route, function(){})
  }
  if(req.query.p){
	app.set("views", req.query.p + "view")
  }else{
	app.set("views", __dirname + "/views")
  } 
  setTimeout(() => {
    res.render('index', { title: 'Hey', message: 'Hello there', place: route, bakery: bread})
  }, 5000)
})

This part is the homepage of my bakery. Here are two key points: The first point is cache(0.1). Combining with the first part, if the hostname of requet doesn’t change, then server would respond with cache even though cache is only stored for 0.1 second. The second point is the if-condition of req.query.p. If you are familiar with the syntax of node.js, then you would know that url accept the GET parameter of p. And with this parameter, you can set the view of your owns. At the end, we can see server sends the data of json format to this page. In conclusion, what we want is bakery.

WTF is 0.1s

In fact, I really expect people come to ask me something like

WTF is 0.1 second for cache ?

or

What we can do with replacing views ?

Here comes the Web cache poisoning which shared by @albinowax in Defcon2018. But the concept of cache is already enough for this challenge even though you didn’t hear the speaking. I will give a picture to make it more clear.

In the picture, after the hacker send the malicious payload to replace the homepage with malicious one. The homepage is the static file, so it can be stored in cache. Furthermore, the hostname of request must be always same, so hacker’s malicious payload can be sent to everyone ….. but only on 0.1 second! Therefore, we can replace the view with the template which would show the content of bakery, and I set cache for ridiculous 0.1 second just because I scare your flag would be shared to others :)

Exploit

You need to upload your malicious homepage first. In the source code, you can find that this server used the template of jade with this line app.set('view engine', 'jade');. After google and can find that writing a jade file is very ez. Here is my simple homepage:

doctype html
html
head
  title my jade template
body
  h1 Hello #{bakery}

For parameter p, with the following source code:

if(req.query.p){
	app.set("views", req.query.p + "view")
  }else{
	app.set("views", __dirname + "/views")

Path should be absolute according to my use of __dirname, but I have given you the path already. For example, my sandbox path is /app/public/OjpmZmZmOjEyOS4xNzQuMTgyLjcw/view, so I would send the url?p=/app/public/OjpmZmZmOjEyOS4xNzQuMTgyLjcw/ in my p. In the end, the page would be replaced with my index.jade just like following picture. Flag gotcha!

However, be careful the filename should be index.jade because I render fixed filename of index in response.

A mysterious present in afternoon

In fact, when I found that still no one solve the challenge in the last three hours, I started to send out a present which was FLAG. In order to help other clients also feel how powerful about cache-posisoning, I write a robot to send out the payload several times. Therefore, you cannot see the flag page which my robot send out, but you can write a loop script to scrape the content of the page, that would be the most tricky way to get the flag.

How do you think of this mysterious present in afternoon ?

Reflection on myself

In fact, I am so imprudent that I don’t make up the exception for some condition. And we also forget to check about the permission issue on server directory. All of these problems cause to the several times of crashing down on server. Therefore, I think for making web challenge, it is very important to limit the user input. Besides, this challenge is really not close to the condition we always face in the real world. For example, no one will store cache for only 0.1 seconds. The functionality of replacing views is also ridiculous. But I still wish you can learn about how cache works for optimizing the web cache, and how hackers can even expand their exploit with web cache. For close more to the real condition, I would likely to choose CDN or nginx next time!

Again thank everyone for attending our PatriotCTF, feel free to leave some feedback :))

Reference

You can find challenge here
Here are some tricks similiar to relentless shell in my note