| tags:Reversing categories:Writeups series:EKOPARTY Pre-CTF
EKOPARTY Pre-CTF - Reversing the APC cache
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}