Keyword
pwnable
python
Analysis
nc 178.128.12.234 10002 or nc 178.128.12.234 10003
This is function x()>>> x
<function x at 0x7f88b0d2e050>
when you access the xoxo service, you can see the above statement by typing ‘x’.
It looks like a python jail.
so basically, you can try typing dir()
or vars()
.
This is function x()>>> dir()
['a', 'xxx']
This is function x()>>> vars()
{'a': 'vars()', 'xxx': 'finding secret in o()'}
You can see that there is a function named o
through the string stored in the variable xxx
.
This is function x()>>> dir(o)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
Some of the attributes that the o
function has are interesting, such as func_code
.
look inside.
This is function x()>>> dir(o.func_code)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
co_code
and co_consts
look nice :P
First, it is the result of co_code
.
This is function x()>>> o.func_code.co_code
d}|jd}d}d}xLtt|D]8}|tt||t||t|A7}q4W||krdGHndGHdS
This is python bytecode.
and, you can decode it.
from pwn import *
import dis
r = remote('178.128.12.234', 10002)
r.recvuntil('>>> ')
r.sendline('o.func_code.co_code')
data = r.recvall()
print dis.dis(data)
0 LOAD_CONST 1 (1)
3 STORE_FAST 1 (1)
6 LOAD_FAST 1 (1)
9 LOAD_ATTR 0 (0)
12 LOAD_CONST 2 (2)
15 CALL_FUNCTION 1
18 STORE_FAST 1 (1)
21 LOAD_CONST 3 (3)
24 STORE_FAST 2 (2)
27 LOAD_CONST 4 (4)
30 STORE_FAST 3 (3)
33 SETUP_LOOP 76 (to 112)
36 LOAD_GLOBAL 1 (1)
39 LOAD_GLOBAL 2 (2)
42 LOAD_FAST 0 (0)
45 CALL_FUNCTION 1
48 CALL_FUNCTION 1
51 GET_ITER
>> 52 FOR_ITER 56 (to 111)
55 STORE_FAST 4 (4)
58 LOAD_FAST 3 (3)
61 LOAD_GLOBAL 3 (3)
64 LOAD_GLOBAL 4 (4)
67 LOAD_FAST 0 (0)
70 LOAD_FAST 4 (4)
73 BINARY_SUBSCR
74 CALL_FUNCTION 1
77 LOAD_GLOBAL 4 (4)
80 LOAD_FAST 2 (2)
83 LOAD_FAST 4 (4)
86 LOAD_GLOBAL 2 (2)
89 LOAD_FAST 0 (0)
92 CALL_FUNCTION 1
95 BINARY_MODULO
96 BINARY_SUBSCR
97 CALL_FUNCTION 1
100 BINARY_XOR
101 CALL_FUNCTION 1
104 INPLACE_ADD
105 STORE_FAST 3 (3)
108 JUMP_ABSOLUTE 52
>> 111 POP_BLOCK
>> 112 LOAD_FAST 3 (3)
115 LOAD_FAST 1 (1)
118 COMPARE_OP 2 (==)
121 POP_JUMP_IF_FALSE 132
124 LOAD_CONST 5 (5)
127 PRINT_ITEM
128 PRINT_NEWLINE
129 JUMP_FORWARD 5 (to 137)
>> 132 LOAD_CONST 6 (6)
135 PRINT_ITEM
136 PRINT_NEWLINE
>> 137 LOAD_CONST 0 (0)
140 RETURN_VALUE
None
Hmm..
Next, it is the result of co_consts
.
This is function x()>>> o.func_code.co_consts
(None, '392a3d3c2b3a22125d58595733031c0c070a043a071a37081d300b1d1f0b09', 'hex', 'pythonwillhelpyouopenthedoor', '', 'Open the door', 'Close the door')
The co_consts
returns a tuple containing the literals used by the bytecode.
Among these, 392a3d3c2b3a22125d58595733031c0c070a043a071a37081d300b1d1f0b09
and pythonwillhelpyouopenthedoor
seem to be suspicious.
(1)
and (3)
in the bytecode mean 392a3d3c2b3a22125d58595733031c0c070a043a071a37081d300b1d1f0b09
and pythonwillhelpyouopenthedoor
. (index 1, index 3)
According to the bytecode, this value must be equal after the XOR
operation.
Solve
Xor the two values.
Rotate if the length is short.
a = '392a3d3c2b3a22125d58595733031c0c070a043a071a37081d300b1d1f0b09'
b = 'pythonwillhelpyouopenthedoor'
b += b[:len(a)/2-len(b)]
a = int(a,16)
b = int(b.encode('hex'),16)
result = hex(a^b)[2:-1].decode('hex')
print result
ISITDTU{1412_secret_in_my_door}