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}