Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrypting single passwords #848

Closed
UncleSpuds opened this issue Jan 16, 2015 · 17 comments
Closed

Decrypting single passwords #848

UncleSpuds opened this issue Jan 16, 2015 · 17 comments
Labels

Comments

@UncleSpuds
Copy link

We had a problem with an upgrade of TeamPass and we are trying to recover the most important passwords that we can't simply change.

I have access to the database and can pull the encrypted passwords out of the database, and I also have the SALT key.

Is there any way to use these two in order to decrypt a few of these?

I'm not much of a PHP person, so trying to learn the methods mcrypt uses and where / how they are applied isn't going so well. I'm just a humble sysadmin who was left with a broken password manager.

Any help would be appreciated.

@nilsteampassnet
Copy link
Owner

Why not to just set it back to the version you were before upgrading?
Use the dump of your database done just before upgrading.
If you do as this you will get back everything and from this you can export your passwords in clear text.

What is your current version?

@UncleSpuds
Copy link
Author

2.1.18 is the version that worked -- and the version we have a dump for (('admin','cpassman_version','2.1.18')). We have tried that version and all versions in between. We have also tried setting TeamPass up on a brand new server.

We are able to dump and recover databases into MySQL without error.

At this point, we're not really trying to get TeamPass working for consumption again-- we're really just trying to salvage the passwords for equipment that do not have a password recovery process so that we do not have to reset pieces of equipment back to default.

I appreciate your help on this.

@nilsteampassnet
Copy link
Owner

If you don't want to put in place TP 2.1.18 on your server, you need to recode a specific page that will be able to read the database and decrypt the passwords using decrypt function in main.functions.php.

@agh1467
Copy link

agh1467 commented Dec 27, 2015

UncleSpuds, I was digging around in TeamPass recently and figured out how to decrypt the passwords in a fairly rudimentary way which could be useful in these types of emergency situations.

There's two steps, first get the passwords out of the database into a file, then parse the file and decrypt the passwords. I put together a command that can do the first part, and wrote a script which can do the second part. It could probably be condensed into all PHP, but I'm not that savvy with it.

Export data from the database into a file:

mysql -u teampass_admin -p teampass -Bse "select teampass_items.id, teampass_items.label, teampass_items.pw, teampass_keys.rand_key FROM teampass_items INNER JOIN teampass_keys ON teampass_items.id=teampass_keys.id;" > db_pws.txt

You'll be prompted to enter the password for the teampass_admin account. This command assumes that the database is called "teampass".

The script:

#!/usr/bin/php
<?php

// Include settings to get the SALT
// Include functions to get decrypt() so to get the PW.
require_once '/var/www/html/teampass/includes/settings.php';
require_once '/var/www/html/teampass/sources/main.functions.php';

// This is a tab delimited file containing four columns: id, label, pw, rand_key
$filename = "db_pws.txt";
// Note: This file can be created with the following command:
// mysql -u teampass_admin -p teampass -Bse \
// "select teampass_items.id, teampass_items.label, teampass_items.pw, teampass_keys.rand_key \
// FROM teampass_items INNER JOIN teampass_keys ON teampass_items.id=teampass_keys.id;"

// Parse the file and put it into an array $records
$contents = file_get_contents($filename);
if ($contents === false) die("Unable to read data file $filename!");
$records = explode("\n", $contents);

// Calculate the number of rows in the file
$records_size = sizeof($records);

// Define some variables used to know how big to make the columns in the table at the end
$pw_maxlen = 0;
$label_maxlen = 0;

// Step through the array and split each row into array $data
for ($i=0; $i<$records_size; $i++) {
  $data[$i] = explode("\t", $records[$i]);

  // If the row has no data, assume it's the last and break out of the for loop
  if ($data[$i][0] == "")  break ;

  // Add another column to the array which contains the decrypted password, stripped of the prepended rand_key
  $data[$i][4] = substr(CleanString(decrypt($data[$i][2])), strlen($data[$i][3]));

  // Set the new maximum length for label and pw if it's greater than the current
  if (strlen($data[$i][1]) > $label_maxlen ) $label_maxlen = strlen($data[$i][1]);
  if (strlen($data[$i][4]) > $pw_maxlen ) $pw_maxlen = strlen($data[$i][4]);
}

for ($i=0; $i<$records_size; $i++) {
  // If the row has no data, assume it's the last and break out of the for loop
  if ($data[$i][0] == "")  break ;

  // Print out in a sort of table all of the labels and PWs defined 
  printf ("%s[%{$label_maxlen}s]%s[%{$pw_maxlen}s]\n", "Label: ", $data[$i][1], " Password: ", $data[$i][4]);
}
?>

This worked for version 2.1.2, but probably newer versions are similar.

@victorpierredev
Copy link

Using version 2.1.19, doesn't work for me. Just outputs Hacking attempt...%.

@brunoscota
Copy link

"Using version 2.1.19, doesn't work for me. Just outputs Hacking attempt...%.

Exact the same to me

@agh1467
Copy link

agh1467 commented Oct 16, 2016

If I recall correctly, I removed the following code from the PHP files that are included in the beginning of the PHP script:

if (!isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1) {
    die('Hacking attempt...');
}

This eliminates the "Hacking attempt..." prompt from appearing. I think it's something to do with cookies, but I don't know.

The PHP files:
/var/www/html/teampass/includes/settings.php
/var/www/html/teampass/sources/main.functions.php

The /var/www/html path may be different on your system.

I also updated the PHP script which now includes the command to export the data, and adds several columns for more completeness:

Folder name (not full path, that's beyond my SQL skillset)
Description
Login
URL

It also uses a tab delimited output which works well with CSV import into Calc, or Excel.

Here is the script:

#!/usr/bin/php
<?php

// Include settings to get the SALT
// Include functions to get decrypt() so to get the PW.
require_once '/var/www/html/teampass/includes/settings.php';
require_once '/var/www/html/teampass/sources/main.functions.php';

// Array columns:
//0 teampass_items.id
//1 teampas_nested_tree.title
//2 teampass_items.label
//3 teampass_items.pw
//4 teampass_keys.rand_key
//5 teampass_items.login
//6 teampass_items.description
//7 teampass_items.url

$exported_data = shell_exec( 'mysql -u teampass_admin -p teampass -Bse "select teampass_items.id, teampass_nested_tree.title, teampass_items.label, teampass_items.pw, teampass_keys.rand_key, teampass_items.login, teampass_items.description, teampass_items.url FROM teampass_items INNER JOIN teampass_keys ON teampass_items.id=teampass_keys.id INNER JOIN teampass_nested_tree ON teampass_items.id_tree = teampass_nested_tree.id;"');

// Put the exported_data into an array
$records = explode("\n", $exported_data);

// Calculate the number of rows in the file
$records_size = sizeof($records);

// Step through the array and split each row into array $data
for ($i=0; $i<$records_size; $i++) {
  $data[$i] = explode("\t", $records[$i]);

  // If the row has no data, assume it's the last and break out of the for loop
  if ($data[$i][0] == "")  break ;

  // Add another column to the array which contains the decrypted password, stripped of the prepended rand_key
  $data[$i][8] = substr(CleanString(decrypt($data[$i][3])), strlen($data[$i][4]));
}

// Print out header column in tab delimited format
printf ("%s\t%s\t%s\t%s\t%s\n", "Folder", "Label", "Login", "Password", "Description", "URL");

for ($i=0; $i<$records_size; $i++) {
  // If the row has no data, assume it's the last and break out of the for loop
  if ($data[$i][0] == "")  break ;

  // Print out rows in a tab delimited format
  printf ("%s\t%s\t%s\t%s\t%s\t%s\n", $data[$i][1], $data[$i][2],$data[$i][5], $data[$i][8], $data[$i][6], $data[$i][7]);
}
?>

This script can be put into a file and redirect the output into a file upon execution at the command line.

@pkoevesdi
Copy link

pkoevesdi commented Jan 5, 2018

I tried it with very easy setup: I just copied a password from my backup.sql (from a teampass_items.pw field) and took the salt from the teampass-seckey.txt (belonging to the backup) and called
decrypt(password,salt);
Isn't this supposed to work? Well, it doesn't. It gets thrown out while verifying the sha256hmac from the encrypted data before trying to decrypt it in main.functions.php
Any hints what I am doing wrong?
What I'm trying to do is get back my passwords. I have a backup.sql with the items in it, and I have the appropriate teampass-seckey.txt. Both from a Teampass 2.1.27.10 install. What else can I do to get back the password from it?

@nilsteampassnet
Copy link
Owner

I don't really understand what is the purpose of this.
Why to try to get out the password from a backup file while you have teampass to access them?

Function decrypt is not used anymore in 2.1.27.10.
So it is normal that the encrypted data is not shown in clear text.

@pkoevesdi
Copy link

pkoevesdi commented Jan 6, 2018

My backup seems to be defective. Everytime I restore it, teampass throws JSON Errors and I can't restore the passwords. Most of them I also have as a clear text backup. But some are missing. Few enough to decrypt them by hand, if possible. I have a well working installation and most of the passwords recovered, so restoriong the few by hand would be the easiest way for me, compared to restoring the whole installation and tracking the JSON errors (which I tried now for several hours unsuccessfully).
So, in 2.1.27.10, which function is used? Is there another way of "manually" (by means of php) decrypt them?
Thank You!

@nilsteampassnet
Copy link
Owner

Function is called cryption

@pkoevesdi
Copy link

Thank You! Works fine now.
For other users:
$b=cryption(password,salt,"decrypt");
$b['string'] now contains the password in cleartext.

@ostrochovsky
Copy link

ostrochovsky commented Feb 18, 2019

this modified version working for TeamPass version 2.1.27.9 (on Linux):

sudo cat /var/teampass/teampass-seckey.txt # get the salt key

sudo mysql # retrieve encrypted password from DB
\r teampass_db
SELECT label,pw FROM teampass_items WHERE login='admin';
\q

php -a # decrypt password using salt key
$password="PASSWORD"
$salt="SALT"
require_once '/var/www/html/teampass/includes/config/settings.php';
require_once '/var/www/html/teampass/sources/main.functions.php'; $b=cryption($password,$salt,"decrypt");
echo $b['string'];
quit

@Pxmme
Copy link

Pxmme commented Mar 19, 2019

Wrote an automation script to decrypt all your passwords in emergency situations :

https://twitter.com/pxmme1337/status/1108054372410376192

Enjoy ;)

@brestows
Copy link

Hi,
I can't decrypt passwords using any of the above methods. How can I decrypt them in 2.1.26 final 3 version ?

@terwarf
Copy link

terwarf commented Apr 29, 2019

Here is my Version that i call regulary from cron (Working with version 2.1.27.33).
It creates a csv file in a backup directory.

<?php

// The secret key
$salt = '';

// Hostname of the database server
$dbHost = 'localhost';

// Username for the database server
$dbUser = '';

// Password for the database server
$dbPass = '';

// Name of the database
$dbName = 'teampass';

// Path and name to the exported csv
// This should not be readable for public,
// but must be writeable for the webserver user
$csvfile = '/tmp/teampassBackup.csv';


$_SESSION['CPM'] = 1; // Don't change this (needed before includes)

// Path to the include files - only change if this script is not in the root dir of teampass
require_once './includes/config/settings.php';
require_once './sources/main.functions.php';

// Dump entries to browser?
// !!! Use only for testing! This is a security risk!!!
$dumpEntries = false;



$dbc = new mysqli( $dbHost, $dbUser, $dbPass, $dbName );
if( $dbc->connect_errno )
{
    die( $dbc->connect_error );
}

$sql = '
    SELECT
         i.id 
        ,i.label
        ,i.description
        ,i.pw
        ,i.url
        ,i.login
        ,i.email
        ,t.title AS foldername
    FROM teampass_items AS i
        LEFT JOIN teampass_nested_tree AS t
            ON i.id_tree = t.id
    ORDER BY i.label ASC';

$res = $dbc->query( $sql );
if( $dbc->errno )
{
    die( $dbc->error );
}

if( $res->num_rows === 0 )
{
    echo 'No Entries found.';
}
else
{
    $csvFilePointer = fopen( $csvfile, 'w' );
    if( $csvFilePointer === false )
    {
        die( 'Opening output file failed.' );
    }

    $writeResult = fputcsv( $csvFilePointer, array('label','description','folder','email','link','login','password') );
    if( $writeResult === false )
    {
        die( 'writing failed.' );
    }

    while( $row = $res->fetch_object() )
    {
        $id = $row->id;
        $encrytedPassword = $row->pw;
        $label = $row->label;
        $description = $row->description;
        $url = $row->url;
        $login = $row->login;
        $email = $row->email;
        $foldername = $row->foldername;

        $cryptResult = cryption( $encrytedPassword, $salt, "decrypt" );
        if( is_array($cryptResult) && isset($cryptResult['string']) )
        {
            $decryptedPassword = $cryptResult['string'];
        }
        else
        {
            echo "Decryption of entry ' . $id . ' failed.\n";
            continue;
        }

        $writeResult = fputcsv( $csvFilePointer, array(
             $label
            ,$description
            ,$foldername
            ,$email
            ,$url
            ,$login
            ,$decryptedPassword
        ));
        if( $writeResult === false )
        {
            die( 'writing failed.' );
        }

        if( $dumpEntries === true )
        {
            echo 'Label: ' . $label . '<br>';
            echo 'Description: ' . $description . '<br>';
            echo 'Folder:' . $foldername . '<br>';
            echo 'Email: ' . $email . '<br>';
            echo 'Link: ' . $url . '<br>';
            echo 'Login: ' . $login . '<br>';
            echo 'Password: ' . $decryptedPassword . '<br>';
            echo '<hr><br>';
        }
    }
}
?>

@mvillodres
Copy link

What we can use to decrypt manually in 3.0?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests