first commit

This commit is contained in:
Ghostie 2025-02-01 21:35:22 +00:00
commit b63786cc92
8 changed files with 620 additions and 0 deletions

274
.clang-format Normal file
View File

@ -0,0 +1,274 @@
---
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: All
AlwaysBreakBeforeMultilineStrings: false
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterExternBlock: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: true
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: AllDefinitions
BreakArrays: true
BreakBeforeBinaryOperators: All
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: GNU
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: MultiLine
ColumnLimit: 79
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: true
AtStartOfFile: true
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: Always
SpaceBeforeParensOptions:
AfterControlStatements: false
AfterForeachMacros: false
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: false
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: c++03
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.o
#*#
*~
bin/beroot

34
Makefile Normal file
View File

@ -0,0 +1,34 @@
CC=gcc
ECHO=echo -e
CFLAGS=-Wall -Werror -std=gnu99 -O0 -g
LIBS=-lcrypt
FILES=build/main.o
OUT=bin/beroot
all: $(FILES)
@$(ECHO) "Linking program"
@$(CC) $(CFLAGS) $(FILES) -o $(OUT) $(LIBS)
@$(ECHO) "Linking finished"
build/main.o: src/main.c src/config.h
@$(ECHO) "CC\t\t"$<
@$(CC) $(CFLAGS) $< -c -o $@ $(LIBS)
install: all
@$(ECHO) "Installing beroot (you MUST be root to install beroot)"
@cp $(OUT) /usr/bin
@chown root:root /usr/bin/beroot # set permissions
@chmod u+s /usr/bin/beroot
@$(ECHO) "Installation successful"
uninstall: all
@$(ECHO) "Uninstalling beroot (you MUST be root to uninstall beroot)"
@rm -f /usr/bin/beroot
@$(ECHO) "Uninstallation successful"
clean:
@$(ECHO) "Cleaning files..."
@rm -f $(FILES) $(OUT)
@$(ECHO) "Cleaning complete :)"

102
Readme.org Normal file
View File

@ -0,0 +1,102 @@
* Beroot
Beroot is just another privilege escalation tool. I made Beroot for two reasons:
- I wanted to learn (I discovered a lot of new functions in the standard
library)
- Sudo is bloated, and wanted to do something smaller - but usable - than doas.
** Usage
Beroot is just two files: ~main.c~ the source of beroot, ~config,h~ the
configurations of the tool. Beroot doesn't have any ~.conf~ files that need to be
parsed or something similar, all the changes are made to the ~config.h~ file.
First, you'll need to build the program:
#+begin_src bash
make
#+end_src
Now, you'll need to login as root to execute the following commands:
#+begin_src bash
make install
#+end_src
That command will copy ~./bin/beroot~ to ~/usr/bin~ and set the appropriate permissions to it.
** Configuration
The only thing you need to modify is the ~permissions~ variable in ~config.h~. This
file contains the following structure:
#+begin_src C
struct user_permissions
{
const char name[16384];
const char target_user[16384];
unsigned char permissions;
};
#+end_src
It only has three fields:
- ~name~: The name of the user you expect can use beroot (like the ones that
belong to the ~wheel~ group)
- ~target_user~: In case you want a user not to be root, but rather a different
user that exists on the machine, you can specify their name using this
variable.
- ~permissions~: Here you can set some of the following permission bits:
- ~PERM_ROOT~: If this is set, the user will execute commands as root,
~target_user~ will be ignored in this case.
- ~PERM_USER~: If this bit is present, the user will execute commands as
~target_user~ instead of root. If both ~PERM_ROOT~ and ~PERM_USER~ are present,
the last one is ignored as ~PERM_ROOT~ is checked first in the code.
- ~PERM_PASSWD~: If this is set, beroot will ask for the target user password
every time that the command is ran.
- ~PERM_NOPASSWD~: If this bit is present, beroot won't ask for the target user
password, it will just execute the specified command. Notice that either
~PERM_PASSWD~ or ~PERM_NOPASSWD~ must be present, if none of them are, beroot
will display an error.
For example, if we have a user called ~lain~ and we want this user to execute
commands as root, and not ask for a password, we'd add the following to the
~permissions~ variable in ~config.h~:
#+begin_src C
const static struct user_permissions permissions[] = {
{ .name = "lain",
.target_user = "\0",
.permissions = PERM_ROOT | PERM_NOPASSWD }
};
#+end_src
In that case, we are setting the user ~lain~ with the permissions of ~PERM_ROOT~
(execute commands as root) and ~PERM_NOPASSWD~ (don't ask for a password).
If we want to have multiple rules, we can just separate them by a comma (,):
#+begin_src C
const static struct user_permissions permissions[]
= { { .name = "lain",
.target_user = "\0",
.permissions = PERM_ROOT | PERM_NOPASSWD },
{ .name = "alyssa",
.target_user = "dbuser",
.permissions = PERM_USER | PERM_PASSWD } };
#+end_src
In that code now we have two users: ~lain~ with the settings we mentioned before,
and a new user ~alyssa~ that will execute commands as ~dbuser~ and ~PERM_PASSWD~ will
make that she has to enter her password every time she runs ~beroot~.
** TODO
I don't want to make this a big program, I want to keep everything as simple as
possible, but there are some things that I want to do:
- Optimise the code, I wrote this in like 1 hour just to test things in the
standard library.
- Some kind of persistency (there can be a ~PERM_PERSIST~ flag) so the password
doesn't need to be entered every time.
- Hide the password while it's being typed.
Those are the only things I have planned adding to beroot.

0
bin/.gitkeep Normal file
View File

0
build/.gitkeep Normal file
View File

25
src/config.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __CONFIG_H
#define __CONFIG_H
#include <limits.h>
#define LOGIN_PASSWD_MAX 16384 // i think this is enough for a password?
#define PERM_ROOT 1 << 0 /* will be the root user */
#define PERM_USER 1 << 1 /* will be a custom specified user */
#define PERM_PASSWD 1 << 2 /* password is required */
#define PERM_NOPASSWD 1 << 3 /* password is not required */
struct user_permissions
{
const char name[LOGIN_NAME_MAX];
const char target_user[LOGIN_NAME_MAX];
unsigned char permissions;
};
const static struct user_permissions permissions[]
= { { .name = "lain",
.target_user = "\0",
.permissions = PERM_ROOT | PERM_NOPASSWD } };
#endif

181
src/main.c Normal file
View File

@ -0,0 +1,181 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <shadow.h>
#include <unistd.h>
#include "config.h"
void
print_usage ()
{
puts ("usage: beroot command [args]");
}
char *
get_current_user_name ()
{
struct passwd *pwd = getpwuid (getuid ());
if (!pwd)
{
puts ("couldn't obtain your pwd");
return NULL;
}
return pwd->pw_name;
}
const struct user_permissions *
user_in_permitted_users (char *name)
{
int total_users
= (int)(sizeof (permissions) / sizeof (struct user_permissions));
for (int i = 0; i < total_users; i++)
{
const struct user_permissions *current_user = &permissions[i];
if (!current_user)
continue;
if (strncmp (name, current_user->name, LOGIN_NAME_MAX) == 0)
return current_user;
}
return NULL;
}
struct spwd *
obtain_target_swpd (const char *name)
{
if (!name)
return NULL;
return getspnam (name);
}
_Bool
authenticate_user (char *password, char *entered_password)
{
// the entered password ends with a \n, we remove it here
entered_password[strcspn (entered_password, "\n")] = '\0';
size_t password_len = strlen (password);
char *encrypted_password = crypt (entered_password, password);
int result = strncmp (password, encrypted_password, password_len);
if (result == 0)
return 1;
return 0;
}
void
execute (char **command)
{
size_t total_commands = 0;
size_t maximum_commands = 10;
char **commands = calloc (sizeof (char *), maximum_commands);
if (!commands)
{
fputs ("Couldn't parse the commands\n", stderr);
return;
}
while (*command != NULL)
{
if (total_commands >= maximum_commands)
{
maximum_commands *= 2;
commands = realloc (commands, sizeof (char *) * maximum_commands);
}
commands[total_commands] = *command;
command++;
total_commands++;
}
commands[total_commands] = NULL;
execvp (commands[0], commands);
free (commands);
commands = NULL;
}
int
main (int argc, char *argv[])
{
if (argc < 2)
{
print_usage ();
return EXIT_FAILURE;
}
char *user_name = get_current_user_name ();
if (!user_name)
return EXIT_FAILURE;
const struct user_permissions *user = user_in_permitted_users (user_name);
if (!user)
{
fprintf (stderr, "%s is not allowed to run commands, aborting\n",
user_name);
return EXIT_FAILURE;
}
struct spwd *target_swpd = user->permissions & PERM_ROOT
? obtain_target_swpd ("root")
: obtain_target_swpd (user->target_user);
if (!target_swpd)
{
fputs ("couldn't obtain the target user's shadow\n", stderr);
return EXIT_FAILURE;
}
struct passwd *target_passwd = getpwnam (target_swpd->sp_namp);
if (!target_passwd)
{
fputs ("couldn't obtain the passwd for the target user\n", stderr);
return EXIT_FAILURE;
}
// TODO: Give the user 3 attempts
_Bool is_authenticated = 0;
if (user->permissions & PERM_NOPASSWD)
{
is_authenticated = 1;
}
else if (user->permissions & PERM_PASSWD)
{
printf ("Enter the %s password: ", user->target_user);
char entered_password[LOGIN_PASSWD_MAX] = { 0 };
fgets (entered_password, LOGIN_PASSWD_MAX - 1, stdin);
is_authenticated
= authenticate_user (target_swpd->sp_pwdp, entered_password);
}
else
{
fputs (
"Please specify an authentication way, either PASSWD or NOPASSWD\n",
stderr);
return 0;
}
if (!is_authenticated)
{
fputs ("Couldn't authenticate you, please try again\n", stderr);
return EXIT_FAILURE;
}
if (setuid (target_passwd->pw_uid) != 0)
{
fputs ("Couldn't switch to the target user\n", stderr);
return EXIT_FAILURE;
}
execute (argv + 1);
return EXIT_SUCCESS;
}