Brian Madden Logo
Your independent source for application and desktop virtualization.
advertisement

Generating APPSRV.INI - passwords?, in the Scripting / Automation forum on BrianMadden.com

rated by 0 users
This post has 13 Replies | 1 Follower

Guest Posted: Tue, Jan 25 2005 7:25 AM
To help ease administration have some scripts which generate APPSRV.INI or .ICA files from user / server data we keep in a simple database.

As most of the files are "just text", it's no problem but there's one particular issue which is storing NT passwords in encypted form, so users can logon transparently - how to generate the encypted password?

Looking at the Metaframe SDK there is a component "MetaFrameCredential" which accepts the original password but there doesn't seem to be a way to get the encypted form out of it. There's also the IMetaFrameWinApp4 Interface which has an "ExportICAFile" method but there's no allowance for storing passwords in there - it creates a generic ICA file for a given published application. Otherwise there doesn't seem to be a useful API for the Citrix client.

Haven't found any other useful pointers apart than a security alert showing how to decrypt a password (http://www.securiteam.com/securitynews/5XQ0H000CK.html) which is the opposite to what I need.

Any ideas? Many thanks.
  • | Post Points: 20
Top 150 Contributor
Points 1,900
Hi

Have done something similar which may or may not help you

Essentially had a client which had MSAM and delivered all web apps via this method. I needed to passthru the user credentials once the initial WI access was granted and the published app icon was executed. As real passthru can only be achieved via the full client I added a encryption string which allowed the user credentials to be encrypted and passed to the 2nd web server which was hosting the web front end. The string essentialy had a time stamp, the encrypted password and the username in clear text. The 2nd web server would decrypt the password and anthenticate over 389.

Relating this to your situation, I think encrypting them is the easy bit, its how the pass thru , assuming you are going to use pass thru, will check the password in the requred format...............

What is the format in which clients will be connecting to the farm? Only via the PN?
  • | Post Points: 35
Guest replied on Tue, Jan 25 2005 11:02 AM
Ah - think I may have explained myself badly (or misunderstood your reply). Only worried about the first layer of authentication (Windows NT) - no need to pass on any login details to the publish application itself.

Basically users only connect using the PN (it's an old Metaframe 1.8 farm - no web servers involved). The Citrix servers validate incoming connections against a Windows NT domain but this should happen invisibly to end users - they should get straight to the published application without needing to manually logi.

When configuring a published application in the Citrix client via the program we add the NT authentication details like;

Properties Login Information User Specified Credentials

... and complete the NT username, password and domain.

This results in something like the following being stored in the APPSRV.INI file, under the given published application section;

Username: someuser
Domain: somentdomain
Password=0009bb885c8a549f458d45

The problem is the password is encrypted by the Citrix client PN (in this example the original password entered was "somepass") and I want to reproduce that encryption so I can bypass using the PN and generate the complete APPSRV.INI file using a script.

That security alert is the only thing I've found online where anyone has done anything like this but it's going in the opposite direction (decrypting) and so far my attempts to understand it and reverse it (using Perl not C) have failed.
  • | Post Points: 20
Top 150 Contributor
Points 1,900
Ok, in that case you will need to find out from Citrix who kind of process they use to generate this encryption...more than likely MD5 etc but the process used and any variables used, if any.

To get a direct answer, Id use your support with Citrix, if you have any, and see what they can do.......but I cant see them revealing that info as your asking for something that may be bespoke and custom to how the client works.........
  • | Post Points: 20
Guest replied on Wed, Feb 9 2005 10:22 PM
/*
icadecrypt.c

Decrypt stored Citrix ICA passwords (in appsrv.ini).

Dug Song
*/

#include
#include
#include
#include
#include

int
hex_decode(char *src, u_char *dst, int outsize)
{
char *p, *pe;
u_char *q, *qe, ch, cl;

pe = src + strlen(src);
qe = dst + outsize;

for (p = src, q = dst; p
ch = tolower(p[0]);
cl = tolower(p[1]);

if ((ch = '0') && (ch
else if ((ch = 'a') && (ch
else return (-1);

if ((cl = '0') && (cl
else if ((cl = 'a') && (cl
else return (-1);

*q++ = (ch <
}
return (q - dst);
}

int
ica_decrypt(u_char *pass, int len)
{
u_short i;
u_char *p, key;

if (len
return (0);

i = ntohs(*(u_short *)pass);

if (i != len - 2)
return (0);

key = pass[2];
p = pass + 3;

for (i -= 2; i 0; i--)
p[i] = p[i - 1] ^ p[i] ^ key;

p[0] ^= (key | 'C');

i = len - 3;
memmove(pass, pass + 3, i);
pass[i] = '\0';

return (1);
}

void
usage(void)
{
fprintf(stderr, "Usage: icadecrypt
exit(1);
}

int
main(int argc, char *argv[])
{
FILE *f;
u_char line[1024], pass[128];
int len;

if (argc != 2 || *argv[1] == '-')
usage();

if ((f = fopen(argv[1], "r")) == NULL) {
perror("fopen");
exit(1);
}
while (fgets(line, sizeof(line), f) != NULL) {
if (strncmp(line, "Password=", 9) == 0) {
len = hex_decode(line + 9, pass, sizeof(pass));
if (ica_decrypt(pass, len))
printf("; icadecrypt: [%s]\n", pass);
}
printf("%s", line);
}
fclose(f);

exit(0);
}

/* 5000. */

  • | Post Points: 5
Guest replied on Tue, Jul 19 2005 2:48 PM
This seems to work for me:

void encrypt(u_char *pass)
{
u_short i,len;
u_char key = 0xeb;

len = strlen(pass)+1;
printf("%.2x%.2x%.2x",0,len,key);
pass[0] ^= (key | 'C');

printf("%.2x",pass[0]);
for(i = 1;i
{
pass[i] ^= pass[i-1] ^ key;
printf("%.2x", pass[i]);
}

printf("\n");
}

( use the includes from the other code. )
I don't know about the key may change on other passwords? as you can see I hard coded it.
  • | Post Points: 20
Not Ranked
Points 20
OK, I had the same problem and I have a bash script and a java program that do this. I've emulated the behavior of the ICA Connection Manager to generate a new random key on each invokation.

Bash script:


#!/bin/sh
read -s -p "Password: " PASS

COUNT=${#PASS}
KEY=$(($RANDOM / 128))
echo

printf %.4x%.2x $(($COUNT +1 )) $KEY
FIRST=1
for ((i = 0; i
CHAR=`echo -n ${PASS:i:1} | od -An -i`
if [ $FIRST ]; then
CH=$(($CHAR ^ ($KEY | 0x43) ))
unset FIRST;
else
CH=$(($CHAR ^ $PREV ^ $KEY))
fi
printf %.2x $CH
PREV=$CH
done
echo


and here comes tha Java method (in Java it's hard to input a password without echoing it to the screen):


public static String encrypt(String str){
int key = new Random().nextInt(255);
StringBuffer hash = new StringBuffer(4 + 2 * (str.length() + 1 ) );
hash.append("0000");
for (int i = 0; i
hash.append("00");
}
String len = Integer.toHexString(str.length() + 1);
int pos = 4;
hash.replace(pos - len.length(), pos, len);

String chString = Integer.toHexString(key);
pos += 2;
hash.replace(pos - chString.length(), pos, chString);
boolean first = true;
byte[] strBytes = str.getBytes();
int ch;
int prev = 0;
byte c;
for (int i = 0; i
c = strBytes[i];
if (first) {
ch = c ^ (key | 0x43);
first = false;
} else {
ch = c ^ prev ^ key;
}
chString = Integer.toHexString(ch);
pos += 2;
hash.replace(pos - chString.length(), pos, chString);
System.out.println(chString);
prev = ch;
}

return hash.toString();
}


Have fun
  • | Post Points: 20
Guest replied on Mon, Oct 23 2006 5:40 AM
Hi!

Could somebody convert this to a vb.net function?
I don't understand java nor c.

Thanks,

Jan
  • | Post Points: 5
Guest replied on Thu, Oct 26 2006 9:13 AM
Now I've converted the java function into a c# class library using MS Java to C# Converter.
Thanks anyway.

Regards,
Jan
  • | Post Points: 5
Guest replied on Tue, Nov 21 2006 1:25 PM
Hello,

This is axactly what I am trying to do. Is your solution still working. So yes how did you achieve this.

User logs on to WI and lauches an application on an other MPS4 server. I would like to pass the credentials into the server where the application is really running.
We are using the ica32t.exe (Minimum Citrix ICA Client for Web browsers) client.

Is your solution still applicable in this case?
  • | Post Points: 5
Guest replied on Tue, Nov 21 2006 6:30 PM
#!/usr/bin/perl -w

$0 =~ s|.*/||;

use Getopt::Long qw(:config gnu_getopt);

GetOptions(
'h|help' = \&usage,
) || usage();

@passwords = (

chomp @passwords;
print citrix_obuscate($_) . "\n"
for ( @passwords );

###############
# Subroutines #
###############

sub citrix_obuscate {
my $p = shift;
my $key = int(rand(256));
# $key = ord('c'); # use this for testing (zzzzzz - 000763190019001900)
my $prev_char = ($key ^ ($key | ord('C')));
my $obfu_str = sprintf("%.4x%.2x", ( length($p) + 1 ), $key);
while ( length $p 0 ) {
my $char = ord( substr($p, 0, 1, '') );
$char = ($char ^ $prev_char ^ $key);
$obfu_str .= sprintf("%.2x", $char);
$prev_char = $char;
} return $obfu_str;
}
sub usage
{
print STDERR <
Note:
This utility will generate a string suitable for use
as a password= value in a Citrix INI or ICA file...
Usage:
$0 -[h|-help] [passwords...]
Examples:
$0 "welcome"
echo "drowssap" | $0
EOF
exit 1;
}

  • | Post Points: 20
Not Ranked
Points 20
' Just a encryption in Visual Basic
Public Function encryptcitrix(password As String) As String
Dim te, sh As String
Dim i, strlen, position, key, prev, c, ch As Integer
Dim first As Boolean
Randomize
key = Int((255 * Rnd) + 1)
te = "0000"
For i = 0 To Len(password)
te = te & "00"
Next

strlen = Len(password) + 1
position = 4
Mid(te, position, 2) = Hex$(strlen)

position = position + 1
Mid(te, position, 2) = Hex$(key)
first = True
prev = 0

For i = 1 To Len(password)
c = Asc(Mid(password, i, 2))
If first Then
ch = c Xor (key Or Asc("C"))
first = False
Else
ch = c Xor prev Xor key
End If
position = position + 2
sh = Hex$(ch)
If Len(sh) = 1 Then sh = "0" & sh
Mid(te, position, 2) = sh
prev = ch
Next
encryptcitrix = LCase(te)
End Function
  • | Post Points: 20
Not Ranked
Points 20
Hello,
Can anyone help me to create the decrypt method in java language ?
I have tried to rewrite an java function taking the c example but I don't succeed because I don't know the function ntohs().

Thanks for your help.
Ric
  • | Post Points: 20
Not Ranked
Points 5
HfX replied on Mon, Nov 24 2008 5:50 AM

Hi, this is a working code for AutoIT. Which is the one I love!

#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.2.12.1
 Author:         Peter M.
 Script Function:
 Encrypt a plain/text password to the style that Citirx Uses in their
 appsrv.ini file or .ica files

 Thanks to Bernhard Ü., Dieter G. and Uli W.
 for helping me!
 Feel free to use this code but keep this header inside your script!
#ce ----------------------------------------------------------------------------
; Script Start - Add your code below here
;Call the function below with the password as string as parameter!
Func _encryptcitrix (ByRef $a)
$password=$a
SRandom(@SEC)
$key=Int((255*Random(0,1,0)+1))

$te="0000"
For $i = 0 To StringLen($password)
 $te = $te & "00"
Next
$strlen = StringLen($password) + 1
$position = 4
$left=StringMid($te,1,$position-1)
$mid=Hex($strlen,8)
For $z = 1 To StringLen ($mid)
 $currstring = StringMid($mid,$z,1)
 If $currstring <> "0" Then
  $mid=StringMid($mid,$z)
  ExitLoop
 EndIf
Next
$dummy=StringLen($mid)
If $dummy > 2 Then $dummy = 2
$right=StringRight($te,((StringLen($te)-StringLen($left))-$dummy))
$te=$left & $mid & $right
$position = $position + 1
$left=StringMid($te,1,$position-1)
$mid=Hex($key)
For $z = 1 To StringLen ($mid)
 $currstring = StringMid($mid,$z,1)
 If $currstring <> "0" Then
  $mid=StringMid($mid,$z)
  ExitLoop
 EndIf
Next
$dummy=StringLen($mid)
If $dummy > 2 Then $dummy = 2
$right=StringRight($te,((StringLen($te)-StringLen($left))-$dummy))
$te = $left & $mid & $right
$first = True
$prev = 0
For $i = 1 To StringLen ($password)
 $c = Asc(StringMid($password,$i,2));checken !!!!
 If $first Then
  $ch = BitXOR ($c,BitOR($key,Asc("C")))
  $first = False
 Else
  $ch = BitXOR($c,$prev,$key)
 EndIf
 $position = $position + 2
 $sh = Hex($ch,2)
 $left=StringMid($te,1,$position-1)
 $mid=$sh
 $dummy=StringLen($mid)
 If $dummy > 2 Then $dummy = 2
 $right=StringRight($te,((StringLen($te)-StringLen($left))-$dummy))
 $te = $left & $mid & $right 
 $prev = $ch
Next
$te=StringLower($te)
Return $te
EndFunc

Thanks to all the previous posters for their code samples.

 

  • | Post Points: 5
Page 1 of 1 (14 items) | RSS