For this challenge we only have an APC dump. APC is a cache for PHP that can cache user data as well as PHP files in form of opcodes.

From the hint we know that APC 3.1.13 with PHP 5.4 are required. I used phpbrew to set-up the required environment.

Normally APC stores data in memory, however it’s possible to dump the cache to a file and later load it. This is our case so we can start playing with APC functions:

<?php
apc_bin_loadfile("cache.data");
var_dump(apc_cache_info());
?>

Now we can see the content of the cache, in particular we can see that the file /var/www/html/login.php is cached.

I then used the vld plugin to get a representation of the code that is executed. This is what we get from this php file:

<?php
apc_bin_loadfile("cache.data");

$_POST['token'] = "testtoken";

include('/var/www/html/login.php');
?>
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /tmp/solve.php
function name:  (null)
number of ops:  7
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   SEND_VAL                                                 'cache.data'
         1        DO_FCALL                                      1          'apc_bin_loadfile'
   5     2        FETCH_W                      global              $1      '_POST'
         3        ASSIGN_DIM                                               $1, 'token'
         4        OP_DATA                                                  'testtoken', $3
   7     5        INCLUDE_OR_EVAL                                          '%2Fvar%2Fwww%2Fhtml%2Flogin.php', INCLUDE
   9     6      > RETURN                                                   1

Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 5, Position 2 = 55
Branch analysis from position: 5
Jump found. Position 1 = 15, Position 2 = 22
Branch analysis from position: 15
Jump found. Position 1 = 23, Position 2 = 29
Branch analysis from position: 23
Jump found. Position 1 = 30, Position 2 = 54
Branch analysis from position: 30
Jump found. Position 1 = 41, Position 2 = 50
Branch analysis from position: 41
Jump found. Position 1 = 53
Branch analysis from position: 53
Jump found. Position 1 = 54
Branch analysis from position: 54
Jump found. Position 1 = 58
Branch analysis from position: 58
Jump found. Position 1 = -2
Branch analysis from position: 50
Jump found. Position 1 = 54
Branch analysis from position: 54
Branch analysis from position: 54
Branch analysis from position: 29
Branch analysis from position: 22
Branch analysis from position: 55
Jump found. Position 1 = -2
filename:       /var/www/html/login.php
function name:  (null)
number of ops:  59
compiled vars:  !0 = $token, !1 = $crypt, !2 = $hash, !3 = $flag
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   3     0  E >   NOP                                                      
  44     1        FETCH_IS                                         $1      '_POST'
         2        ISSET_ISEMPTY_DIM_OBJ                       16777216  ~2      $1, 'token'
         3        BOOL_NOT                                         ~3      ~2
         4      > JMPZ                                                     ~3, ->55
  45     5    >   FETCH_R                      global              $4      '_POST'
         6        FETCH_DIM_R                                      $5      $4, 'token'
         7        ASSIGN                                                   !0, $5
  47     8        INIT_FCALL_BY_NAME                                       'substr'
         9        SEND_VAR                                                 !0
        10        SEND_VAL                                                 0
        11        SEND_VAL                                                 4
        12        DO_FCALL_BY_NAME                              3  $7      
        13        IS_IDENTICAL                                     ~8      $7, 'CmxQ'
        14      > JMPZ_EX                                          ~8      ~8, ->22
        15    >   INIT_FCALL_BY_NAME                                       'substr'
        16        SEND_VAR                                                 !0
        17        SEND_VAL                                                 44
        18        SEND_VAL                                                 4
        19        DO_FCALL_BY_NAME                              3  $9      
        20        IS_IDENTICAL                                     ~10     $9, 'MgY%2F'
        21        BOOL                                             ~8      ~10
        22    > > JMPZ_EX                                          ~8      ~8, ->29
        23    >   INIT_FCALL_BY_NAME                                       'substr'
        24        SEND_VAR                                                 !0
        25        SEND_VAL                                                 -4
        26        DO_FCALL_BY_NAME                              2  $11     
        27        IS_IDENTICAL                                     ~12     $11, 'Mg%3D%3D'
        28        BOOL                                             ~8      ~12
        29    > > JMPZ                                                     ~8, ->54
  48    30    >   FETCH_CLASS                                   4  :13     'AzDGCrypt'
        31        NEW                                              $14     :13
        32        SEND_VAL                                                 'EKO%7Bthis_is_not_the_flag%7D'
        33        DO_FCALL_BY_NAME                              1          
        34        ASSIGN                                                   !1, $14
  49    35        INIT_METHOD_CALL                                         !1, 'decrypt'
        36        SEND_VAR                                                 !0
        37        DO_FCALL_BY_NAME                              1  $18     
        38        ASSIGN                                                   !2, $18
  51    39        IS_IDENTICAL                                     ~20     !2, 'e88ef51d4112b999380444ce48488762'
        40      > JMPZ                                                     ~20, ->50
  52    41    >   INIT_FCALL_BY_NAME                                       'sha1'
        42        SEND_VAR                                                 !0
        43        DO_FCALL_BY_NAME                              1  $21     
        44        ASSIGN                                                   !3, $21
  53    45        ADD_STRING                                       ~23     'Welcome+master%2C+your+key+is+EKO%7B'
        46        ADD_VAR                                          ~23     ~23, !3
        47        ADD_CHAR                                         ~23     ~23, 125
        48        ECHO                                                     ~23
  54    49      > JMP                                                      ->53
  55    50    >   INIT_FCALL_BY_NAME                                       'header'
        51        SEND_VAL                                                 'Location%3A+index.php'
        52        DO_FCALL_BY_NAME                              1          
  57    53    > > JMP                                                      ->54
  58    54    > > JMP                                                      ->58
  59    55    >   INIT_FCALL_BY_NAME                                       'header'
        56        SEND_VAL                                                 'Location%3A+index.php'
        57        DO_FCALL_BY_NAME                              1          
  61    58    > > RETURN                                                   1

Class AzDGCrypt:
Function azdgcrypt:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /var/www/html/login.php
function name:  AzDGCrypt
number of ops:  4
compiled vars:  !0 = $m
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   5     0  E >   RECV                                             !0      
   6     1        ASSIGN_OBJ                                               'k'
         2        OP_DATA                                                  !0
   7     3      > RETURN                                                   null

End of function azdgcrypt

Function ed:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 39, Position 2 = 17
Branch analysis from position: 39
Jump found. Position 1 = -2
Branch analysis from position: 17
Jump found. Position 1 = 22, Position 2 = 24
Branch analysis from position: 22
Jump found. Position 1 = 24
Branch analysis from position: 24
Jump found. Position 1 = 14
Branch analysis from position: 14
Jump found. Position 1 = 9
Branch analysis from position: 9
Branch analysis from position: 24
filename:       /var/www/html/login.php
function name:  ed
number of ops:  41
compiled vars:  !0 = $t, !1 = $r, !2 = $c, !3 = $v, !4 = $i
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   8     0  E >   RECV                                             !0      
   9     1        INIT_FCALL_BY_NAME                                       'md5'
         2        FETCH_OBJ_FUNC_ARG                               $0      'k'
         3        SEND_VAR                                                 $0
         4        DO_FCALL_BY_NAME                              1  $1      
         5        ASSIGN                                                   !1, $1
  10     6        ASSIGN                                                   !2, 0
  11     7        ASSIGN                                                   !3, ''
  12     8        ASSIGN                                                   !4, 0
         9    >   INIT_FCALL_BY_NAME                                       'strlen'
        10        SEND_VAR                                                 !0
        11        DO_FCALL_BY_NAME                              1  $6      
        12        IS_SMALLER                                       ~7      !4, $6
        13      > JMPZNZ                                       17          ~7, ->39
        14    >   POST_INC                                         ~8      !4
        15        FREE                                                     ~8
        16      > JMP                                                      ->9
  13    17    >   INIT_FCALL_BY_NAME                                       'strlen'
        18        SEND_VAR                                                 !1
        19        DO_FCALL_BY_NAME                              1  $9      
        20        IS_EQUAL                                         ~10     !2, $9
        21      > JMPZ                                                     ~10, ->24
        22    >   ASSIGN                                                   !2, 0
        23      > JMP                                                      ->24
  14    24    >   INIT_FCALL_BY_NAME                                       'substr'
        25        SEND_VAR                                                 !0
        26        SEND_VAR                                                 !4
        27        SEND_VAL                                                 1
        28        DO_FCALL_BY_NAME                              3  $12     
        29        INIT_FCALL_BY_NAME                                       'substr'
        30        SEND_VAR                                                 !1
        31        SEND_VAR                                                 !2
        32        SEND_VAL                                                 1
        33        DO_FCALL_BY_NAME                              3  $13     
        34        BW_XOR                                           ~14     $12, $13
        35        ASSIGN_CONCAT                                 0          !3, ~14
  15    36        POST_INC                                         ~16     !2
        37        FREE                                                     ~16
  16    38      > JMP                                                      ->14
  17    39    > > RETURN                                                   !3
  18    40*     > RETURN                                                   null

End of function ed

Function crypt:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 55, Position 2 = 27
Branch analysis from position: 55
Jump found. Position 1 = -2
Branch analysis from position: 27
Jump found. Position 1 = 32, Position 2 = 34
Branch analysis from position: 32
Jump found. Position 1 = 34
Branch analysis from position: 34
Jump found. Position 1 = 24
Branch analysis from position: 24
Jump found. Position 1 = 19
Branch analysis from position: 19
Branch analysis from position: 34
filename:       /var/www/html/login.php
function name:  crypt
number of ops:  63
compiled vars:  !0 = $t, !1 = $r, !2 = $c, !3 = $v, !4 = $i
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  19     0  E >   RECV                                             !0      
  20     1        INIT_FCALL_BY_NAME                                       'srand'
         2        INIT_FCALL_BY_NAME                                       'microtime'
         3        DO_FCALL_BY_NAME                              0  $0      
         4        CAST                                          2  ~1      $0
         5        MUL                                              ~2      ~1, 1000000
         6        SEND_VAL                                                 ~2
         7        DO_FCALL_BY_NAME                              1          
  21     8        INIT_FCALL_BY_NAME                                       'md5'
         9        INIT_FCALL_BY_NAME                                       'rand'
        10        SEND_VAL                                                 0
        11        SEND_VAL                                                 32000
        12        DO_FCALL_BY_NAME                              2  $4      
        13        SEND_VAR_NO_REF                               4          $4
        14        DO_FCALL_BY_NAME                              1  $5      
        15        ASSIGN                                                   !1, $5
  22    16        ASSIGN                                                   !2, 0
  23    17        ASSIGN                                                   !3, ''
  24    18        ASSIGN                                                   !4, 0
        19    >   INIT_FCALL_BY_NAME                                       'strlen'
        20        SEND_VAR                                                 !0
        21        DO_FCALL_BY_NAME                              1  $10     
        22        IS_SMALLER                                       ~11     !4, $10
        23      > JMPZNZ                                       27          ~11, ->55
        24    >   POST_INC                                         ~12     !4
        25        FREE                                                     ~12
        26      > JMP                                                      ->19
  25    27    >   INIT_FCALL_BY_NAME                                       'strlen'
        28        SEND_VAR                                                 !1
        29        DO_FCALL_BY_NAME                              1  $13     
        30        IS_EQUAL                                         ~14     !2, $13
        31      > JMPZ                                                     ~14, ->34
        32    >   ASSIGN                                                   !2, 0
        33      > JMP                                                      ->34
  26    34    >   INIT_FCALL_BY_NAME                                       'substr'
        35        SEND_VAR                                                 !1
        36        SEND_VAR                                                 !2
        37        SEND_VAL                                                 1
        38        DO_FCALL_BY_NAME                              3  $16     
  27    39        INIT_FCALL_BY_NAME                                       'substr'
        40        SEND_VAR                                                 !0
        41        SEND_VAR                                                 !4
        42        SEND_VAL                                                 1
        43        DO_FCALL_BY_NAME                              3  $17     
        44        INIT_FCALL_BY_NAME                                       'substr'
        45        SEND_VAR                                                 !1
        46        SEND_VAR                                                 !2
        47        SEND_VAL                                                 1
        48        DO_FCALL_BY_NAME                              3  $18     
        49        BW_XOR                                           ~19     $17, $18
        50        CONCAT                                           ~20     $16, ~19
        51        ASSIGN_CONCAT                                 0          !3, ~20
  28    52        POST_INC                                         ~22     !2
        53        FREE                                                     ~22
  29    54      > JMP                                                      ->24
  30    55    >   INIT_FCALL_BY_NAME                                       'base64_encode'
        56        INIT_METHOD_CALL                                         'ed'
        57        SEND_VAR                                                 !3
        58        DO_FCALL_BY_NAME                              1  $24     
        59        SEND_VAR_NO_REF                               4          $24
        60        DO_FCALL_BY_NAME                              1  $25     
        61      > RETURN                                                   $25
  31    62*     > RETURN                                                   null

End of function crypt

Function decrypt:
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 34, Position 2 = 18
Branch analysis from position: 34
Jump found. Position 1 = -2
Branch analysis from position: 18
Jump found. Position 1 = 15
Branch analysis from position: 15
Jump found. Position 1 = 10
Branch analysis from position: 10
filename:       /var/www/html/login.php
function name:  decrypt
number of ops:  36
compiled vars:  !0 = $t, !1 = $v, !2 = $i, !3 = $md5
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  32     0  E >   RECV                                             !0      
  33     1        INIT_METHOD_CALL                                         'ed'
         2        INIT_FCALL_BY_NAME                                       'base64_decode'
         3        SEND_VAR                                                 !0
         4        DO_FCALL_BY_NAME                              1  $1      
         5        SEND_VAR_NO_REF                               4          $1
         6        DO_FCALL_BY_NAME                              1  $2      
         7        ASSIGN                                                   !0, $2
  34     8        ASSIGN                                                   !1, ''
  35     9        ASSIGN                                                   !2, 0
        10    >   INIT_FCALL_BY_NAME                                       'strlen'
        11        SEND_VAR                                                 !0
        12        DO_FCALL_BY_NAME                              1  $6      
        13        IS_SMALLER                                       ~7      !2, $6
        14      > JMPZNZ                                       18          ~7, ->34
        15    >   POST_INC                                         ~8      !2
        16        FREE                                                     ~8
        17      > JMP                                                      ->10
  36    18    >   INIT_FCALL_BY_NAME                                       'substr'
        19        SEND_VAR                                                 !0
        20        SEND_VAR                                                 !2
        21        SEND_VAL                                                 1
        22        DO_FCALL_BY_NAME                              3  $9      
        23        ASSIGN                                                   !3, $9
  37    24        POST_INC                                         ~11     !2
        25        FREE                                                     ~11
  38    26        INIT_FCALL_BY_NAME                                       'substr'
        27        SEND_VAR                                                 !0
        28        SEND_VAR                                                 !2
        29        SEND_VAL                                                 1
        30        DO_FCALL_BY_NAME                              3  $12     
        31        BW_XOR                                           ~13     $12, !3
        32        ASSIGN_CONCAT                                 0          !1, ~13
  39    33      > JMP                                                      ->15
  40    34    > > RETURN                                                   !1
  41    35*     > RETURN                                                   null

End of function decrypt

End of class AzDGCrypt.

branch: #  0; line:     2-    9; sop:     0; eop:     6; out1:  -2
path #1: 0, 
branch: #  0; line:     3-   44; sop:     0; eop:     4; out1:   5; out2:  55
branch: #  5; line:    45-   47; sop:     5; eop:    14; out1:  15; out2:  22
branch: # 15; line:    47-   47; sop:    15; eop:    21; out1:  22
branch: # 22; line:    47-   47; sop:    22; eop:    22; out1:  23; out2:  29
branch: # 23; line:    47-   47; sop:    23; eop:    28; out1:  29
branch: # 29; line:    47-   47; sop:    29; eop:    29; out1:  30; out2:  54
branch: # 30; line:    48-   51; sop:    30; eop:    40; out1:  41; out2:  50
branch: # 41; line:    52-   54; sop:    41; eop:    49; out1:  53
branch: # 50; line:    55-   57; sop:    50; eop:    52; out1:  53
branch: # 53; line:    57-   57; sop:    53; eop:    53; out1:  54
branch: # 54; line:    58-   58; sop:    54; eop:    54; out1:  58
branch: # 55; line:    59-   61; sop:    55; eop:    57; out1:  58
branch: # 58; line:    61-   61; sop:    58; eop:    58; out1:  -2
path #1: 0, 5, 15, 22, 23, 29, 30, 41, 53, 54, 58, 
path #2: 0, 5, 15, 22, 23, 29, 30, 50, 53, 54, 58, 
path #3: 0, 5, 15, 22, 23, 29, 54, 58, 
path #4: 0, 5, 15, 22, 29, 30, 41, 53, 54, 58, 
path #5: 0, 5, 15, 22, 29, 30, 50, 53, 54, 58, 
path #6: 0, 5, 15, 22, 29, 54, 58, 
path #7: 0, 5, 22, 23, 29, 30, 41, 53, 54, 58, 
path #8: 0, 5, 22, 23, 29, 30, 50, 53, 54, 58, 
path #9: 0, 5, 22, 23, 29, 54, 58, 
path #10: 0, 5, 22, 29, 30, 41, 53, 54, 58, 
path #11: 0, 5, 22, 29, 30, 50, 53, 54, 58, 
path #12: 0, 5, 22, 29, 54, 58, 
path #13: 0, 55, 58, 
branch: #  0; line:     5-    7; sop:     0; eop:     3; out1:  -2
path #1: 0, 
branch: #  0; line:     8-   12; sop:     0; eop:     8; out1:   9
branch: #  9; line:    12-   12; sop:     9; eop:    13; out1:  39; out2:  17
branch: # 14; line:    12-   12; sop:    14; eop:    16; out1:   9
branch: # 17; line:    13-   13; sop:    17; eop:    21; out1:  22; out2:  24
branch: # 22; line:    13-   13; sop:    22; eop:    23; out1:  24
branch: # 24; line:    14-   16; sop:    24; eop:    38; out1:  14
branch: # 39; line:    17-   18; sop:    39; eop:    40
path #1: 0, 9, 39, 
path #2: 0, 9, 17, 22, 24, 14, 9, 39, 
path #3: 0, 9, 17, 24, 14, 9, 39, 
branch: #  0; line:    19-   24; sop:     0; eop:    18; out1:  19
branch: # 19; line:    24-   24; sop:    19; eop:    23; out1:  55; out2:  27
branch: # 24; line:    24-   24; sop:    24; eop:    26; out1:  19
branch: # 27; line:    25-   25; sop:    27; eop:    31; out1:  32; out2:  34
branch: # 32; line:    25-   25; sop:    32; eop:    33; out1:  34
branch: # 34; line:    26-   29; sop:    34; eop:    54; out1:  24
branch: # 55; line:    30-   31; sop:    55; eop:    62
path #1: 0, 19, 55, 
path #2: 0, 19, 27, 32, 34, 24, 19, 55, 
path #3: 0, 19, 27, 34, 24, 19, 55, 
branch: #  0; line:    32-   35; sop:     0; eop:     9; out1:  10
branch: # 10; line:    35-   35; sop:    10; eop:    14; out1:  34; out2:  18
branch: # 15; line:    35-   35; sop:    15; eop:    17; out1:  10
branch: # 18; line:    36-   39; sop:    18; eop:    33; out1:  15
branch: # 34; line:    40-   41; sop:    34; eop:    35
path #1: 0, 10, 34, 
path #2: 0, 10, 18, 15, 10, 34, 

We can start reversing from here. First we notice that the login page gets a token as input through POST. We can also notice that the AzDGCrypt class is used, we can find its source on github. The key used for the class constructor is “EKO{this_is_not_the_flag}” and our token is passed as input to the decrypt method. The decryption needs to match “e88ef51d4112b999380444ce48488762”. Also, the token needs to start with “CmxQ”, at position 44 it needs to have “MgY/” and end with “Mg==”. This is needed because, if we look at the AzDGCrypt class, we can notice that the encryption function uses a random number from 0 to 32000.

We can modify the crypt function to get the random value as argument:

[...]
function crypt( $t, $counter )
{
    $r = md5( $counter );
[...]

And use the following code to compute all the possible encryptions of the required string:

$cr64 = new AzDGCrypt("EKO{this_is_not_the_flag}");
for ($k=0;$k<32001;$k++) {
    $d = $cr64->crypt("e88ef51d4112b999380444ce48488762", $k);
    echo $d."\n";
}

Later we can filter those values to get the one that passes the checks and feed it to the login page:

<?php
apc_bin_loadfile("cache.data");

$_POST['token'] = "CmxQaQAzBTYKZQYzAWAFZAY1V2NVZAZgUGQCbAU9Vz4CMgY/AzgLaQwxWm5SYVY2UGNUaVFlAm5VO1MzBDNQMg==";

include('/var/www/html/login.php');
?>
Welcome master, your key is EKO {59a59936b318e8ef20fd923a3e7b05a1e44e9e91}

fox