├── LICENSE ├── README.md ├── demo └── phpinfo.php └── phpfun.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Alan Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHPFun: ([.^]) 2 | 3 | Inspired by [aemkei/jsfuck](http://github.com/aemkei/jsfuck) and based on [splitline/PHPFuck](https://github.com/splitline/PHPFuck) 4 | 5 | Using only 6 different characters to write and execute PHP. 6 | 7 | Only support PHP 7+ currently. 8 | 9 | ## Demo 10 | The following source will execute `phpinfo();`: 11 | 12 | ```php 13 | 14 | ``` 15 | 16 | ## Usage 17 | 18 | ``` 19 | usage: phpfun.py [-h] [-O FILE] [-P] [-E {assert,create_function}] code 20 | 21 | positional arguments: 22 | code any string to encode. 23 | 24 | optional arguments: 25 | -h, --help show this help message and exit 26 | -O FILE, --output-file FILE 27 | write encoded string into some file. 28 | -P, --plain-string encode as plain string (without eval it). 29 | -E {assert,create_function}, --eval {assert,create_function} 30 | choose eval mode. (`assert` mode only support PHP < 7.1) 31 | ``` 32 | 33 | You can just use it like this: `python3 phpfun.py "system('id');"` 34 | 35 | ### Arguments 36 | - code (required) 37 | - Any string or php code to encode. 38 | - -O, --output-file 39 | - Write encoded string into some file. 40 | - -P, --plain-string 41 | - Encode as plain string (without eval it). 42 | - With this argument, I will not wrap your code into `assert` or `create_function` to eval. 43 | - -E, --eval 44 | - You can choose your eval mode! 45 | - `create_function` mode (default) 46 | - `create_function('', YOUR_CODE)();` 47 | - `assert` mode 48 | - Only support PHP < 7.1 (=7.0.x). 49 | - `assert( '(function(){ YOUR_CODE; return 1; })()' );` 50 | 51 | ## TODO 52 | - [ ] Don't use deprecated feature. (`create_function` has been DEPRECATED) 53 | - [ ] Compatible with PHP 8 54 | - [ ] With `[^a-zA-Z0-9]` and len(set(encoded)) < 6 55 | -------------------------------------------------------------------------------- /demo/phpinfo.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /phpfun.py: -------------------------------------------------------------------------------- 1 | # PHPFun: ([.^]) 2 | 3 | from argparse import ArgumentParser 4 | 5 | 6 | class PHPFun: 7 | def __init__(self): 8 | # simple constant 9 | arr_str = "[].[]" # "ArrayArray" 10 | 11 | # generate digits 12 | nums = {0: "[]^[]", 1: "[]^[[]]"} 13 | self.char_mapping = dict() 14 | self.char_mapping['A'] = f"({arr_str})[[]]" 15 | self.char_mapping['r'] = f"({arr_str})[[[]]]" 16 | nums[3] = f"{self.char_mapping['A']}^{self.char_mapping['r']}" # "A" ^ "r" 17 | nums[2] = f"({arr_str})[[]]^({arr_str})[[[]]]^[[]]" # "3" ^ [[]] 18 | self.char_mapping['y'] = f"(({nums[0]}).{arr_str})[({nums[1]}).({nums[0]})]" 19 | nums[8] = f"{self.char_mapping['A']}^{self.char_mapping['y']}" # "A" ^ "y" 20 | nums[4] = f"{nums[8]}^[]^(({nums[1]}).({nums[2]}))" 21 | nums[5] = f"{nums[4]}^[[]]" 22 | nums[6] = f"{nums[2]}^({nums[4]})" 23 | nums[7] = f"{nums[3]}^({nums[4]})" 24 | nums[9] = f"{nums[8]}^[[]]" 25 | 26 | self.nums = nums # Note: 3 and 8 are string 27 | # using `Aray0123456789` & xor to generate printable ascii char 28 | self.char_mapping.update({ 29 | '\t': f'({nums[1]}).[]^({arr_str})[[]]^({arr_str})[{nums[4]}]', 30 | '\n': f'({nums[2]}).[]^({arr_str})[[]]^({arr_str})[{nums[4]}]', 31 | '\x0b': f'({arr_str})[[[]]]^({arr_str})[{nums[4]}]', 32 | '\x0c': f'({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[4]}]', 33 | '\r': f'({nums[5]}).[]^({arr_str})[[]]^({arr_str})[{nums[4]}]', 34 | ' ': f'({arr_str})[[]]^({arr_str})[{nums[3]}]', 35 | '!': f'({nums[2]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 36 | '"': f'({nums[1]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 37 | '#': f'({nums[0]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 38 | '$': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]', 39 | '%': f'({nums[1]}).[]^({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]', 40 | '&': f'({nums[5]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 41 | "'": f'({nums[4]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 42 | '(': f'({nums[0]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 43 | ')': f'({nums[1]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 44 | '*': f'({nums[2]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 45 | '+': f'({arr_str})[[]]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 46 | ',': f'({nums[4]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 47 | '-': f'({nums[5]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 48 | '.': f'({nums[6]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 49 | '/': f'({nums[7]}).[]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 50 | '0': f'{nums[0]}', 51 | '1': f'{nums[1]}', 52 | '2': f'{nums[2]}', 53 | '3': f'{nums[3]}', 54 | '4': f'{nums[4]}', 55 | '5': f'{nums[5]}', 56 | '6': f'{nums[6]}', 57 | '7': f'{nums[7]}', 58 | '8': f'{nums[8]}', 59 | '9': f'{nums[9]}', 60 | ':': f'({nums[1]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[4]}]', 61 | ';': f'({nums[0]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[4]}]', 62 | '<': f'({nums[0]}).[]^({nums[4]}).[][[]]^({arr_str})[[]]^({arr_str})[{nums[4]}]', 63 | '=': f'({nums[1]}).[]^({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[4]}]', 64 | '>': f'({nums[5]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[4]}]', 65 | '?': f'({nums[4]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[4]}]', 66 | '@': f'({nums[2]}).[]^({arr_str})[[[]]]', 67 | # 'A': f"({arr_str})[[]]", 68 | 'B': f'({nums[0]}).[]^({arr_str})[[[]]]', 69 | 'C': f'({nums[1]}).[]^({arr_str})[[[]]]', 70 | 'D': f'({nums[1]}).[]^({nums[4]}).[]^({arr_str})[[]]', 71 | 'E': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[[]]', 72 | 'F': f'({nums[4]}).[]^({arr_str})[[[]]]', 73 | 'G': f'({nums[5]}).[]^({arr_str})[[[]]]', 74 | 'H': f'({nums[1]}).[]^({arr_str})[{nums[4]}]', 75 | 'I': f'({nums[0]}).[]^({arr_str})[{nums[4]}]', 76 | 'J': f'({arr_str})[[]]^({arr_str})[[[]]]^({arr_str})[{nums[4]}]', 77 | 'K': f'({nums[2]}).[]^({arr_str})[{nums[4]}]', 78 | 'L': f'({nums[5]}).[]^({arr_str})[{nums[4]}]', 79 | 'M': f'({nums[4]}).[]^({arr_str})[{nums[4]}]', 80 | 'N': f'({nums[7]}).[]^({arr_str})[{nums[4]}]', 81 | 'O': f'({nums[6]}).[]^({arr_str})[{nums[4]}]', 82 | 'P': f'({nums[1]}).[]^({arr_str})[{nums[3]}]', 83 | 'Q': f'({nums[0]}).[]^({arr_str})[{nums[3]}]', 84 | 'R': f'({arr_str})[[]]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 85 | 'S': f'({nums[2]}).[]^({arr_str})[{nums[3]}]', 86 | 'T': f'({nums[5]}).[]^({arr_str})[{nums[3]}]', 87 | 'U': f'({nums[4]}).[]^({arr_str})[{nums[3]}]', 88 | 'V': f'({nums[7]}).[]^({arr_str})[{nums[3]}]', 89 | 'W': f'({nums[6]}).[]^({arr_str})[{nums[3]}]', 90 | 'X': f'({nums[2]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 91 | 'Y': f'({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 92 | 'Z': f'({nums[0]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 93 | '[': f'({nums[1]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 94 | '\\': f'({nums[1]}).[]^({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 95 | ']': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 96 | '^': f'({nums[4]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 97 | '_': f'({nums[5]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 98 | '`': f'({nums[0]}).[]^({nums[1]}).[]^({arr_str})[{nums[3]}]', 99 | 'a': f'({arr_str})[{nums[3]}]', 100 | 'b': f'({nums[1]}).[]^({nums[2]}).[]^({arr_str})[{nums[3]}]', 101 | 'c': f'({nums[0]}).[]^({nums[2]}).[]^({arr_str})[{nums[3]}]', 102 | 'd': f'({nums[1]}).[]^({nums[4]}).[]^({arr_str})[{nums[3]}]', 103 | 'e': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[{nums[3]}]', 104 | 'f': f'({nums[4]}).[]^({arr_str})[[]]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]', 105 | 'g': f'({nums[2]}).[]^({nums[4]}).[]^({arr_str})[{nums[3]}]', 106 | 'h': f'({nums[1]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 107 | 'i': f'({nums[0]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 108 | 'j': f'({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 109 | 'k': f'({nums[2]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 110 | 'l': f'({nums[5]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 111 | 'm': f'({nums[4]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 112 | 'n': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[[[]]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 113 | 'o': f'({nums[6]}).[]^({arr_str})[[]]^({arr_str})[{nums[3]}]^({arr_str})[{nums[4]}]', 114 | 'p': f'({nums[1]}).[]^({arr_str})[[]]', 115 | 'q': f'({nums[0]}).[]^({arr_str})[[]]', 116 | # 'r': f'({arr_str})[[[]]]', 117 | 's': f'({nums[2]}).[]^({arr_str})[[]]', 118 | 't': f'({nums[5]}).[]^({arr_str})[[]]', 119 | 'u': f'({nums[4]}).[]^({arr_str})[[]]', 120 | 'v': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[[[]]]', 121 | 'w': f'({nums[6]}).[]^({arr_str})[[]]', 122 | 'x': f'({nums[0]}).[]^({nums[1]}).[]^({arr_str})[{nums[4]}]', 123 | # 'y': f"(({nums[0]}).{arr_str})[({nums[1]}).({nums[0]})]", 124 | 'z': f'({nums[1]}).[]^({nums[2]}).[]^({arr_str})[{nums[4]}]', 125 | '{': f'({nums[0]}).[]^({nums[2]}).[]^({arr_str})[{nums[4]}]', 126 | '|': f'({nums[1]}).[]^({nums[4]}).[]^({arr_str})[{nums[4]}]', 127 | '}': f'({nums[0]}).[]^({nums[4]}).[]^({arr_str})[{nums[4]}]', 128 | '~': f'({nums[4]}).[]^({arr_str})[[]]^({arr_str})[[[]]]^({arr_str})[{nums[4]}]'}) 129 | 130 | def encode(self, code, eval_mode=None): 131 | def clean_code(code): 132 | return code.replace('\n', '').replace(' ', '') 133 | 134 | def basic_encode(code): 135 | return '.'.join( 136 | [f"({self.char_mapping[c] if c in self.char_mapping else fix_missing_char(c)})" for c in code]) 137 | 138 | def encode_number(num): 139 | return f"{self.nums[0]}+({'.'.join([self.nums[int(n)] for n in str(num)])})" 140 | 141 | def fix_missing_char(char, compatiable=True): 142 | # to compatiable with PHP < 7.2.0: `mb_chr` only support PHP >= 7.2.0 143 | if compatiable: 144 | str_getcsv = basic_encode("str_getcsv") 145 | mb_chr = basic_encode('IntlChar,chr') 146 | char_code = encode_number(ord(char)) 147 | return f"({str_getcsv})({mb_chr})({char_code})" 148 | else: 149 | return f"({basic_encode('mb_chr')})({basic_encode(str(ord(char)))})" 150 | 151 | if eval_mode == 'create_function': 152 | code = code.replace('"', '""') 153 | 154 | code = basic_encode(code) 155 | 156 | if not eval_mode: 157 | return code 158 | 159 | elif eval_mode == 'create_function': 160 | create_function = basic_encode("create_function") 161 | str_getcsv = basic_encode("str_getcsv") 162 | comma = basic_encode(",") 163 | quote = basic_encode('"') 164 | 165 | """ 166 | 1. create_function(...str_getcsv(',"YOUR_CODE"') ) 167 | 2. create_function(...['', 'YOURCODE']) 168 | 3. create_function('', 'YOURCODE') 169 | """ 170 | 171 | eval_code = f"""({create_function})( 172 | ...({str_getcsv})({comma}.{quote}.{code}.{quote}) 173 | )() 174 | """ 175 | 176 | elif eval_mode == 'assert': # only support PHP < 7.1 177 | assert_func = basic_encode('assert') 178 | prefix = basic_encode('(function(){') 179 | postfix = basic_encode(';return 1;})()') 180 | eval_code = f""" 181 | ({assert_func})( 182 | ({prefix}).({code}).({postfix}) 183 | ) 184 | """ 185 | 186 | return clean_code(eval_code) 187 | 188 | 189 | if __name__ == "__main__": 190 | parser = ArgumentParser() 191 | parser.add_argument("code", help="any string to encode.") 192 | parser.add_argument("-O", "--output-file", dest="file", 193 | help="write encoded string into some file.") 194 | parser.add_argument("-P", "--plain-string", dest="plain", action='store_true', 195 | help="encode as plain string (without eval it).") 196 | parser.add_argument("-E", "--eval", dest="eval", 197 | choices=['assert', 'create_function'], default='create_function', 198 | help="choose eval mode. (`assert` mode only support PHP < 7.1)") 199 | args = parser.parse_args() 200 | 201 | code = args.code 202 | 203 | phpfun = PHPFun() 204 | 205 | if args.plain: 206 | encoded = phpfun.encode(code) 207 | assert (set(encoded) <= set('([.^])')) 208 | else: 209 | encoded = phpfun.encode(code, args.eval) 210 | assert (set(encoded[:-1]) <= set('([.^])')) 211 | encoded = "\n" 212 | 213 | if args.file: 214 | with open(args.file, 'w') as f: 215 | f.write(encoded) 216 | else: 217 | print(encoded) 218 | 219 | if args.plain: 220 | print(f"Output as plain string, without eval it.") 221 | else: 222 | print(f"Using `{args.eval}` mode to eval.") 223 | 224 | print(len(encoded), "chars.") 225 | --------------------------------------------------------------------------------