1<?php 2 3$socket = null; 4$errno = 0; 5$context = stream_context_create(array('ssl' => array('local_cert' => dirname(__FILE__).'/cert.pem'))); 6 7for ($i=0; $i<10 && !$socket; ++$i) { 8 $port = rand(50000, 65535); 9 10 $socket = @stream_socket_server("tcp://127.0.0.1:$port", $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); 11} 12//set anther random port that is not the same as $port 13do{ 14 $pasv_port = rand(50000, 65535); 15}while($pasv_port == $port); 16 17if (!$socket) { 18 echo "$errstr ($errno)\n"; 19 die("could not start/bind the ftp server\n"); 20} 21 22$pid = pcntl_fork(); 23 24function pasv_listen($action){ 25 global $pasv_port, $tmp_file; 26 $tmp_file = 'nm2.php'; 27 $pid = pcntl_fork(); 28 if($pid === 0){ 29 $soc = stream_socket_server("tcp://127.0.0.1:$pasv_port"); 30 $fs = stream_socket_accept($soc, 3); 31 switch ($action) { 32 case 'fget': 33 case 'get': 34 //listen for 3 seconds 3 seconds 35 fputs($fs, "I am passive.\r\n"); 36 break; 37 case 'put': 38 file_put_contents($tmp_file, stream_get_contents($fs)); 39 break; 40 case 'list': 41 fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 .\r\n"); 42 fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 ..\r\n"); 43 fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 public_ftp\r\n"); 44 break; 45 case 'list_null': 46 fputs($fs, "\r\n"); 47 break; 48 } 49 fclose($fs); 50 exit; 51 } 52} 53 54if ($pid) { 55 56 function dump_and_exit($buf) 57 { 58 var_dump($buf); 59 fclose($GLOBALS['s']); 60 exit; 61 } 62 63 function anonymous() 64 { 65 return $GLOBALS['user'] === 'anonymous'; 66 } 67 68 /* quick&dirty realpath() like function */ 69 function change_dir($dir) 70 { 71 global $cwd; 72 73 if ($dir[0] == '/') { 74 $cwd = $dir; 75 return; 76 } 77 78 $cwd = "$cwd/$dir"; 79 80 do { 81 $old = $cwd; 82 $cwd = preg_replace('@/?[^/]+/\.\.@', '', $cwd); 83 } while ($old != $cwd); 84 85 $cwd = strtr($cwd, array('//' => '/')); 86 if (!$cwd) $cwd = '/'; 87 } 88 89 $s = stream_socket_accept($socket); 90 91 if (!$s) die("Error accepting a new connection\n"); 92 93 fputs($s, "220----- PHP FTP server 0.3 -----\r\n220 Service ready\r\n"); 94 $buf = fread($s, 2048); 95 96 97 function user_auth($buf) { 98 global $user, $s, $ssl, $bug37799; 99 100 if (!empty($ssl)) { 101 if ($buf !== "AUTH TLS\r\n") { 102 fputs($s, "500 Syntax error, command unrecognized.\r\n"); 103 dump_and_exit($buf); 104 } 105 106 if (empty($bug37799)) { 107 fputs($s, "234 auth type accepted\r\n"); 108 } else { 109 fputs($s, "666 dummy\r\n"); 110 fputs($s, "666 bogus msg\r\n"); 111 exit; 112 } 113 114 if (!stream_socket_enable_crypto($s, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER)) { 115 die("SSLv23 handshake failed.\n"); 116 } 117 118 if (!preg_match('/^PBSZ \d+\r\n$/', $buf = fread($s, 2048))) { 119 fputs($s, "501 bogus data\r\n"); 120 dump_and_exit($buf); 121 } 122 123 fputs($s, "200 OK\r\n"); 124 $buf = fread($s, 2048); 125 126 if ($buf !== "PROT P\r\n") { 127 fputs($s, "504 Wrong protection.\r\n"); 128 dump_and_exit($buf); 129 } 130 131 fputs($s, "200 OK\r\n"); 132 133 $buf = fread($s, 2048); 134 } 135 136 if ($buf == "AUTH TLS\r\n") { 137 fputs($s, "500 not supported.\r\n"); 138 return ; 139 } else if (!preg_match('/^USER (\w+)\r\n$/', $buf, $m)) { 140 fputs($s, "500 Syntax error, command unrecognized.\r\n"); 141 dump_and_exit($buf); 142 } 143 $user = $m[1]; 144 if ($user !== 'user' && $user !== 'anonymous') { 145 fputs($s, "530 Not logged in.\r\n"); 146 fclose($s); 147 exit; 148 } 149 150 if (anonymous()) { 151 fputs($s, "230 Anonymous user logged in\r\n"); 152 153 } else { 154 fputs($s, "331 User name ok, need password\r\n"); 155 156 if (!preg_match('/^PASS (\w+)\r\n$/', $buf = fread($s, 100), $m)) { 157 fputs($s, "500 Syntax error, command unrecognized.\r\n"); 158 dump_and_exit($buf); 159 } 160 161 $pass = $m[1]; 162 if ($pass === 'pass') { 163 fputs($s, "230 User logged in\r\n"); 164 } else { 165 fputs($s, "530 Not logged in.\r\n"); 166 fclose($s); 167 exit; 168 } 169 } 170 } 171 172 user_auth($buf); 173 174 $cwd = '/'; 175 $num_bogus_cmds = 0; 176 177 while($buf = fread($s, 4098)) { 178 if (!empty($bogus)) { 179 fputs($s, "502 Command not implemented (".$num_bogus_cmds++.").\r\n"); 180 181 } else if ($buf === "HELP\r\n") { 182 fputs($s, "214-There is help available for the following commands:\r\n"); 183 fputs($s, " USER\r\n"); 184 fputs($s, " HELP\r\n"); 185 fputs($s, "214 end of list\r\n"); 186 187 } elseif ($buf === "HELP HELP\r\n") { 188 fputs($s, "214 Syntax: HELP [<SP> <string>] <CRLF>\r\n"); 189 190 } elseif ($buf === "PWD\r\n") { 191 fputs($s, "257 \"$cwd\" is current directory.\r\n"); 192 193 } elseif ($buf === "CDUP\r\n") { 194 change_dir('..'); 195 fputs($s, "250 CDUP command successful.\r\n"); 196 197 } elseif ($buf === "SYST\r\n") { 198 if (isset($bug27809)) { 199 fputs($s, "215 OS/400 is the remote operating system. The TCP/IP version is \"V5R2M0\"\r\n"); 200 } else { 201 fputs($s, "215 UNIX Type: L8.\r\n"); 202 } 203 204 } elseif ($buf === "TYPE A\r\n") { 205 $ascii = true; 206 fputs($s, "200 OK\r\n"); 207 208 } elseif ($buf === "AUTH SSL\r\n") { 209 $ascii = true; 210 fputs($s, "500 not supported\r\n"); 211 212 } elseif ($buf === "TYPE I\r\n") { 213 $ascii = false; 214 fputs($s, "200 OK\r\n"); 215 216 } elseif ($buf === "QUIT\r\n") { 217 break; 218 219 } elseif (preg_match("~^PORT (\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\r\n$~", $buf, $m)) { 220 $host = "$m[1].$m[2].$m[3].$m[4]"; 221 $port = ((int)$m[5] << 8) + (int)$m[6]; 222 fputs($s, "200 OK.\r\n"); 223 224 } elseif (preg_match("~^STOR ([\w/.-]+)\r\n$~", $buf, $m)) { 225 fputs($s, "150 File status okay; about to open data connection\r\n"); 226 227 if(empty($pasv)) 228 { 229 if (!$fs = stream_socket_client("tcp://$host:$port")) { 230 fputs($s, "425 Can't open data connection\r\n"); 231 continue; 232 } 233 234 $data = stream_get_contents($fs); 235 $orig = file_get_contents(dirname(__FILE__).'/'.$m[1]); 236 237 238 if (isset($ascii) && !$ascii && $orig === $data) { 239 fputs($s, "226 Closing data Connection.\r\n"); 240 241 } elseif ((!empty($ascii) || isset($bug39583)) && $data === strtr($orig, array("\r\n" => "\n", "\r" => "\n", "\n" => "\r\n"))) { 242 fputs($s, "226 Closing data Connection.\r\n"); 243 244 } else { 245 var_dump($data); 246 var_dump($orig); 247 fputs($s, "552 Requested file action aborted.\r\n"); 248 } 249 fclose($fs); 250 }else{ 251 $data = file_get_contents('nm2.php'); 252 $orig = file_get_contents(dirname(__FILE__).'/'.$m[1]); 253 if ( $orig === $data) { 254 fputs($s, "226 Closing data Connection.\r\n"); 255 256 } else { 257 var_dump($data); 258 var_dump($orig); 259 fputs($s, "552 Requested file action aborted.\r\n"); 260 } 261 } 262 263 } elseif (preg_match("~^APPE ([\w/.-]+)\r\n$~", $buf, $m)) { 264 fputs($s, "150 File status okay; about to open data connection\r\n"); 265 266 if(empty($pasv)) 267 { 268 if (!$fs = stream_socket_client("tcp://$host:$port")) { 269 fputs($s, "425 Can't open data connection\r\n"); 270 continue; 271 } 272 273 $data = stream_get_contents($fs); 274 file_put_contents(__DIR__.'/'.$m[1], $data, FILE_APPEND); 275 fputs($s, "226 Closing data Connection.\r\n"); 276 fclose($fs); 277 }else{ 278 $data = stream_get_contents($fs); 279 file_put_contents(__DIR__.'/'.$m[1], $data, FILE_APPEND); 280 fputs($s, "226 Closing data Connection.\r\n"); 281 fclose($fs); 282 } 283 284 }elseif (preg_match("~^CWD ([A-Za-z./]+)\r\n$~", $buf, $m)) { 285 if (isset($bug77680)) { 286 fputs($s, "550 Directory change to $m[1] failed: file does not exist\r\n"); 287 var_dump($buf); 288 } else { 289 change_dir($m[1]); 290 fputs($s, "250 CWD command successful.\r\n"); 291 } 292 293 } elseif (preg_match("~^NLST(?: ([A-Za-z./]+))?\r\n$~", $buf, $m)) { 294 295 if (isset($m[1]) && (($m[1] === 'bogusdir') || ($m[1] === '/bogusdir'))) { 296 fputs($s, "250 $m[1]: No such file or directory\r\n"); 297 continue; 298 } 299 300 // there are some servers that don't open the ftp-data socket if there's nothing to send 301 if (isset($bug39458) && isset($m[1]) && $m[1] === 'emptydir') { 302 fputs($s, "226 Transfer complete.\r\n"); 303 continue; 304 } 305 306 if (empty($pasv)) { 307 fputs($s, "150 File status okay; about to open data connection\r\n"); 308 if (!$fs = stream_socket_client("tcp://$host:$port")) { 309 fputs($s, "425 Can't open data connection\r\n"); 310 continue; 311 } 312 } else { 313 fputs($s, "125 Data connection already open; transfer starting.\r\n"); 314 $fs=$pasvs; 315 } 316 317 318 if ((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) { 319 die("SSLv23 handshake failed.\n"); 320 } 321 322 if (empty($m[1]) || $m[1] !== 'emptydir') { 323 fputs($fs, "file1\r\nfile1\r\nfile\nb0rk\r\n"); 324 } 325 326 fputs($s, "226 Closing data Connection.\r\n"); 327 fclose($fs); 328 329 } elseif (preg_match("~^MKD ([A-Za-z./]+)\r\n$~", $buf, $m)) { 330 if (isset($bug7216)) { 331 fputs($s, "257 OK.\r\n"); 332 } else { 333 if (isset($bug77680)) { 334 var_dump($buf); 335 } 336 fputs($s, "257 \"/path/to/ftproot$cwd$m[1]\" created.\r\n"); 337 } 338 339 } elseif (preg_match('/^USER /', $buf)) { 340 user_auth($buf); 341 342 } elseif (preg_match('/^MDTM ([\w\h]+)/', $buf, $matches)) { 343 switch ($matches [1]){ 344 case "A": 345 fputs($s, "213 19980615100045.014\r\n"); 346 break; 347 case "B": 348 fputs($s, "213 19980615100045.014\r\n"); 349 break; 350 case "C": 351 fputs($s, "213 19980705132316\r\n"); 352 break; 353 case "19990929043300 File6": 354 fputs($s, "213 19991005213102\r\n"); 355 break; 356 default : 357 fputs($s, "550 No file named \"{$matches [1]}\"\r\n"); 358 break; 359 } 360 }elseif (preg_match('/^RETR ([\/]*[\w\h]+)/', $buf, $matches)) { 361 if(!empty($pasv)){ 362 ; 363 } 364 else if (!$fs = stream_socket_client("tcp://$host:$port")) { 365 fputs($s, "425 Can't open data connection\r\n"); 366 continue; 367 } 368 369 switch($matches[1]){ 370 371 case "pasv": 372 fputs($s, "150 File status okay; about to open data connection.\r\n"); 373 //the data connection is handled in another forked process 374 // called from outside this while loop 375 fputs($s, "226 Closing data Connection.\r\n"); 376 break; 377 case "a story": 378 fputs($s, "150 File status okay; about to open data connection.\r\n"); 379 fputs($fs, "For sale: baby shoes, never worn.\r\n"); 380 fputs($s, "226 Closing data Connection.\r\n"); 381 break; 382 case "binary data": 383 fputs($s, "150 File status okay; about to open data connection.\r\n"); 384 $transfer_type = $ascii? 'ASCII' : 'BINARY' ; 385 fputs($fs, $transfer_type."Foo\0Bar\r\n"); 386 fputs($s, "226 Closing data Connection.\r\n"); 387 break; 388 case "fget": 389 fputs($s, "150 File status okay; about to open data connection.\r\n"); 390 $transfer_type = $ascii? 'ASCII' : 'BINARY' ; 391 fputs($fs, $transfer_type."FooBar\r\n"); 392 fputs($s, "226 Closing data Connection.\r\n"); 393 break; 394 case "fgetresume": 395 fputs($s, "150 File status okay; about to open data connection.\r\n"); 396 $transfer_type = $ascii? 'ASCII' : 'BINARY' ; 397 fputs($fs, "Bar\r\n"); 398 fputs($s, "226 Closing data Connection.\r\n"); 399 break; 400 case "fget_large": 401 fputs($s, "150 File status okay; about to open data connection.\r\n"); 402 $transfer_type = $ascii? 'ASCII' : 'BINARY' ; 403 if ($GLOBALS['rest_pos'] == '5368709119') { 404 fputs($fs, "X"); 405 } else { 406 fputs($fs, "Y"); 407 } 408 fputs($s, "226 Closing data Connection.\r\n"); 409 break; 410 case "mediumfile": 411 fputs($s, "150 File status okay; about to open data connection.\r\n"); 412 for($i = 0; $i < 150; $i++){ 413 fputs($fs, "This is line $i of the test data.\n"); 414 } 415 fputs($s, "226 Closing data Connection.\r\n"); 416 break; 417 case "/bug73457": 418 fputs($s, "150 File status okay; about to open data connection.\r\n"); 419 break; 420 421 default: 422 fputs($s, "550 {$matches[1]}: No such file or directory \r\n"); 423 break; 424 } 425 if(isset($fs)) 426 fclose($fs); 427 428 429 }elseif (preg_match('/^PASV/', $buf, $matches)) { 430 $pasv=true; 431 $host = "127.0.0.1"; 432 $i=0; 433 434 if (empty($bug73457)) { 435 do { 436 if (!empty($ssl)) { 437 $soc = @stream_socket_server("tcp://127.0.0.1:$pasv_port", $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); 438 } else { 439 $soc = @stream_socket_server("tcp://127.0.0.1:$pasv_port"); 440 } 441 /* Could bind port, Try another port */ 442 if (!$soc) { 443 $pasv_port = rand(50000, 65535); 444 } 445 $i++; 446 } while ($i<10 && !$soc); 447 448 if (!$soc) { 449 echo "$errstr ($errno)\n"; 450 die("could not bind passive port\n"); 451 } 452 } else { 453 $pasv_port=1234; 454 } 455 456 $p2 = $pasv_port % ((int) 1 << 8); 457 $p1 = ($pasv_port-$p2)/((int) 1 << 8); 458 fputs($s, "227 Entering Passive Mode. (127,0,0,1,{$p1},{$p2})\r\n"); 459 460 if (empty($bug73457)) { 461 $pasvs = stream_socket_accept($soc,10); 462 } 463 464 } elseif (preg_match('/^EPSV/', $buf, $matches)) { 465 fputs($s, "550 Extended passsive mode not supported.\r\n"); 466 } elseif (preg_match('/^SITE EXEC/', $buf, $matches)) { 467 fputs($s, "200 OK\r\n"); 468 469 } elseif (preg_match('/^RMD/', $buf, $matches)) { 470 fputs($s, "250 OK\r\n"); 471 472 } elseif (preg_match('/^SITE CHMOD/', $buf, $matches)) { 473 fputs($s, "200 OK\r\n"); 474 475 } elseif (preg_match('/^DELE/', $buf, $matches)) { 476 fputs($s, "250 OK\r\n"); 477 478 } elseif (preg_match('/^ALLO (\d+)/', $buf, $matches)) { 479 fputs($s, "200 " . $matches[1] . " bytes allocated\r\n"); 480 481 }elseif (preg_match('/^LIST www\//', $buf, $matches)) { 482 fputs($s, "226 Transfer complete\r\n"); 483 484 }elseif (preg_match('/^LIST no_exists\//', $buf, $matches)) { 485 fputs($s, "425 Error establishing connection\r\n"); 486 487 }elseif (preg_match('/^REST (\d+)/', $buf, $matches)) { 488 $GLOBALS['rest_pos'] = $matches[1]; 489 fputs($s, "350 OK\r\n"); 490 }elseif (preg_match('/^SIZE largefile/', $buf)) { 491 fputs($s, "213 5368709120\r\n"); 492 }elseif (preg_match('/^RNFR existing_file/', $buf, $matches)) { 493 fputs($s, "350 File or directory exists, ready for destination name\r\n"); 494 }elseif (preg_match('/^RNFR nonexisting_file/', $buf, $matches)) { 495 fputs($s, "550 No such file or directory\r\n"); 496 }elseif (preg_match('/^RNTO nonexisting_file/', $buf, $matches)) { 497 fputs($s, "250 Rename successful\r\n"); 498 }elseif (preg_match('/^MLSD no_exists\//', $buf, $matches)) { 499 fputs($s, "425 Error establishing connection\r\n"); 500 }elseif (preg_match("~^MLSD(?: ([A-Za-z./]+))?\r\n$~", $buf, $m)) { 501 502 if(isset($m[1]) && (($m[1] === 'bogusdir') || ($m[1] === '/bogusdir'))) { 503 fputs($s, "250 $m[1]: No such file or directory\r\n"); 504 continue; 505 } 506 507 // there are some servers that don't open the ftp-data socket if there's nothing to send 508 if(isset($bug39458) && isset($m[1]) && $m[1] === 'emptydir') { 509 fputs($s, "226 Transfer complete.\r\n"); 510 continue; 511 } 512 513 if(empty($pasv)) { 514 fputs($s, "150 File status okay; about to open data connection\r\n"); 515 if(!$fs = stream_socket_client("tcp://$host:$port")) { 516 fputs($s, "425 Can't open data connection\r\n"); 517 continue; 518 } 519 } else { 520 fputs($s, "125 Data connection already open; transfer starting.\r\n"); 521 $fs = $pasvs; 522 } 523 524 if((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, TRUE, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) { 525 die("SSLv23 handshake failed.\n"); 526 } 527 528 if(empty($m[1]) || $m[1] !== 'emptydir') { 529 fputs($fs, "modify=20170127230002;perm=flcdmpe;type=cdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; .\r\n"); 530 fputs($fs, "modify=20170127230002;perm=flcdmpe;type=pdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; ..\r\n"); 531 fputs($fs, "modify=20170126121225;perm=adfrw;size=4729;type=file;unique=811U4340CB9;UNIX.group=33;UNIX.mode=0644;UNIX.owner=33; foobar\r\n"); 532 fputs($fs, "fact=val=ue;empty=; path;name\r\n"); 533 fputs($fs, "no_space\r\n"); 534 fputs($fs, "no_semi pathname\r\n"); 535 fputs($fs, "no_eq; pathname\r\n"); 536 } 537 538 fputs($s, "226 Closing data Connection.\r\n"); 539 fclose($fs); 540 }elseif (preg_match('/^SIZE \/bug73457/', $buf)) { 541 fputs($s, "213 10\r\n"); 542 }elseif (preg_match("/^SITE/", $buf)) { 543 fputs($s, "500 Syntax error, command unrecognized.\r\n"); 544 }else { 545 dump_and_exit($buf); 546 } 547 } 548 fclose($s); 549 exit; 550} 551 552fclose($socket); 553?> 554