@@ -1091,12 +1091,14 @@ def upload_manually(self, data, target_path = './payload', prompt = b'$', chunk_
10911091 ```
10921092 loop:
10931093 echo <chunk> | base64 -d >> <target_path>.<compression>
1094- <compression> -d <target_path>.<compression>
1094+ <compression> -d -f <target_path>.<compression>
10951095 chmod <chmod_flags> <target_path>
10961096 ```
10971097
10981098 It is assumed that a `base64` command is available on the target system.
1099- When ``compression`` is ``auto`` the best compression utility available is chosen.
1099+ When ``compression`` is ``auto`` the best compression utility available
1100+ between ``gzip`` and ``xz`` is chosen with a fallback to uncompressed
1101+ upload.
11001102
11011103 Arguments:
11021104
@@ -1108,6 +1110,28 @@ def upload_manually(self, data, target_path = './payload', prompt = b'$', chunk_
11081110 compression(str): The compression to use. ``auto`` to automatically choose the best compression or ``gzip`` or ``xz``.
11091111 end_marker(str): The marker to use to detect the end of the output. Only used when prompt is not set.
11101112
1113+ Examples:
1114+
1115+ >>> l = listen()
1116+ >>> l.spawn_process('/bin/sh')
1117+ >>> r = remote('127.0.0.1', l.lport)
1118+ >>> r.upload_manually(b'some\xca \xfe data\n ', prompt=b'', chmod_flags='')
1119+ >>> r.sendline(b'cat ./payload')
1120+ >>> r.recvline()
1121+ b'some\xca \xfe data\n '
1122+
1123+ >>> r.upload_manually(cyclic(0x1000), target_path='./cyclic_pattern', prompt=b'', chunk_size=0x10, compression='gzip')
1124+ >>> r.sendline(b'sha256sum ./cyclic_pattern')
1125+ >>> r.recvlineS(keepends=False).startswith(sha256sumhex(cyclic(0x1000)))
1126+ True
1127+
1128+ >>> blob = ELF.from_assembly(shellcraft.echo('Hello world!\n ') + shellcraft.exit(0))
1129+ >>> r.upload_manually(blob.data, prompt=b'')
1130+ >>> r.sendline(b'./payload')
1131+ >>> r.recvline()
1132+ b'Hello world!\n '
1133+ >>> r.close()
1134+ >>> l.close()
11111135 """
11121136 echo_end = ""
11131137 if not prompt :
@@ -1125,7 +1149,7 @@ def upload_manually(self, data, target_path = './payload', prompt = b'$', chunk_
11251149 self .sendline ("echo {}" .format (end_marker ).encode ())
11261150 if compression == 'auto' :
11271151 for utility in possible_compression :
1128- self .sendlineafter (end_markerb , "command -v {} && echo YEP || echo NOPE; {}" .format (utility , echo_end ).encode ())
1152+ self .sendlineafter (end_markerb , "command -v {} && echo YEP || echo NOPE{}" .format (utility , echo_end ).encode ())
11291153 result = self .recvuntil ([b'YEP' , b'NOPE' ])
11301154 if b'YEP' in result :
11311155 compression_mode = utility
@@ -1137,33 +1161,44 @@ def upload_manually(self, data, target_path = './payload', prompt = b'$', chunk_
11371161
11381162 self .debug ('Manually uploading using compression mode: %s' , compression_mode )
11391163
1164+ compressed_data = b''
11401165 if compression_mode == 'xz' :
11411166 import lzma
1142- data = lzma .compress (data , format = lzma .FORMAT_XZ , preset = 9 )
1167+ compressed_data = lzma .compress (data , format = lzma .FORMAT_XZ , preset = 9 )
11431168 compressed_path = target_path + '.xz'
11441169 elif compression_mode == 'gzip' :
11451170 import gzip
1146- data = gzip .compress (data , compresslevel = 9 )
1171+ compressed_data = gzip .compress (data , compresslevel = 9 )
11471172 compressed_path = target_path + '.gz'
11481173 else :
11491174 compressed_path = target_path
1175+
1176+ # Don't compress if it doesn't reduce the size.
1177+ if len (compressed_data ) >= len (data ):
1178+ compression_mode = None
1179+ compressed_path = target_path
1180+ else :
1181+ data = compressed_data
11501182
11511183 # Upload data in `chunk_size` chunks. Assume base64 is available.
11521184 with self .progress ('Uploading payload' ) as p :
11531185 for idx , chunk in enumerate (iters .group (chunk_size , data )):
1186+ if None in chunk :
1187+ chunk = chunk [:chunk .index (None )]
11541188 if idx == 0 :
1155- self .sendlineafter (end_markerb , "echo {} | base64 -d > {}{}" .format (fiddling .b64e (chunk ), compressed_path , echo_end ).encode ())
1189+ self .sendlineafter (end_markerb , "echo {} | base64 -d > {}{}" .format (fiddling .b64e (bytes ( chunk ) ), compressed_path , echo_end ).encode ())
11561190 else :
1157- self .sendlineafter (end_markerb , "echo {} | base64 -d >> {}{}" .format (fiddling .b64e (chunk ), compressed_path , echo_end ).encode ())
1158- p .status ('{}/{}' .format (idx , len (data )// chunk_size ))
1191+ self .sendlineafter (end_markerb , "echo {} | base64 -d >> {}{}" .format (fiddling .b64e (bytes (chunk )), compressed_path , echo_end ).encode ())
1192+ p .status ('{}/{} {}' .format (idx + 1 , len (data )// chunk_size + 1 , misc .size (idx * chunk_size + len (chunk ))))
1193+ p .success (misc .size (len (data )))
11591194
11601195 # Decompress the file and set the permissions.
11611196 if compression_mode is not None :
1162- self .sendlineafter (end_markerb , '{} -d {}{}' .format (compression_mode , compressed_path , echo_end ).encode ())
1197+ self .sendlineafter (end_markerb , '{} -d -f {}{}' .format (compression_mode , compressed_path , echo_end ).encode ())
11631198 if chmod_flags :
11641199 self .sendlineafter (end_markerb , 'chmod {} {}{}' .format (chmod_flags , target_path , echo_end ).encode ())
11651200 if not prompt :
1166- self .recvuntil (end_markerb )
1201+ self .recvuntil (end_markerb + b' \n ' )
11671202
11681203 def connect_input (self , other ):
11691204 """connect_input(other)
0 commit comments