Blog: How Tos

How to reverse Wyse terminal password crypto: Method One

consultant-placeholder12 Michael Yardley 09 Feb 2016

(Credit also due to @tautology0).

Thin-clients are an increasingly popular option for providing access to virtualised infrastructure. This is typically in the form of a full-desktop-environment or standalone published applications (e.g. Notepad / Microsoft Office applications / Internet browsers).

Despite the many advantages of this common technology, we wanted to talk today about a negative point we have recently observed in one particular implementation. Introducing, the Wyse thin-client:


It’s rare that we do not find some form of thin-client technology in use on a corporate network. Wyse is by far the most commonly encountered, meaning the below login screen is an all too familiar sight:


During infrastructure tests, the FTP server used by the Wyse thin-clients will be flagged as supporting anonymous authentication. So naturally, we take a look at the contents of the FTP server, leading to yet another all too familiar sight:


To provide some background, Wyse thin-clients are initially ‘dumb’ and rely on the file ‘wnos.ini’ for their configuration. Here is a typical example of what happens when a thin-client is powered on:

  1. Client configures basic network access via DHCP
  2. Client checks for DHCP options 161/162 to identify the FTP server
  3. Client pulls wnos.ini from the FTP server using anonymous authentication

As standard course of action, any consultant will check for sensitive information within any identified configuration files. There have been numerous occasions where we have found Domain Administrator credentials hard-coded (in plaintext!) in various forms of configuration files.

Below is an example of a wnos.ini configuration file. The values for the username and password have been highlighted:

;* *
;* This wnos.ini file was generated with the *
;* Configuration Generator 7.4.02 *
;* Copyright by Thomas Moellerbernd *
;* *
;* *
;* *

;* General 1 *

AdminMode=yes Admin-Username=MEAFMNABMK Admin-Password=MEAFMNABMKFOMJFP
Privilege=None HideSysInfo=yes HidePPP=yes EnableNetworkTest=yes ShowDisplaySettings=DDC-Only

;* Wireless / 802.1x *

~truncated for brevity~

A few things catch our eye:

  1. The password always seems to be stored using the same encoding format (Uppercase alpha)
  2. On this occasion, the ‘Admin-Password’ string has data repeated from the ‘Admin-Username’ string
  3. The configuration file references the tool used to generate it! Can we use the tool to reverse the password?

A brief search online uncovers numerous hits, mostly on tech support forums, of network admins uploading their wnos.ini configuration files. Curiously, the majority of these wnos.ini files are uploaded with the ‘Admin-Password’ value!

Our first port of call was to download the ‘Configuration Generator’ referenced by the configuration. The wnos.ini file imported cleanly. We instantly noticed the ‘Encrypted Admin Password’ field being populated:


The tool also came packaged with some other files, including a win32 binary named ‘CryptGui.exe’:

Configuration Generator.exe

Running this file from the command line produced some interesting results:

C:toolswyse>CryptGui.exe password
Number of command line parameters = 1

The exe appears to take an input and reflect it back along with, what appears to be, 2 different encoded versions of the password! Most interesting is the highlighted string, which bares resemblance to the string in the wnos.ini file we have.

We can go for a cryptanalysis technique of trying to work out the password by feeding it basic known strings and looking for patterns (in fact there’s a very good write up of doing this for a similar encoding technique by dscuetz here).

Here are some basic samples on the output:


What we can see is that each character is represented by two upper case letter and that characters are static for every other place, look at ‘aaa’ and ‘bbb’ and we see the pattern XXYYXX; but in ‘bab’ we see different letters than we expect.

This leads to the suspicion that it’s a static key, unsalted and that the encoding method uses two (or more) characters to produce the encoded version.

We wanted to step back slightly, and see if the bundled configuration generator could be used to decode/decrypt passwords. So we loaded up the wnos.ini file within the configuration generator and attached a debugger:


I wish we could post a heroic tale of coaxing the tool to slowly divulge its secrets, alas, after a very short period of time we found the encoded password value hiding away in memory:


And right next to the encoded password, is something that looks highly like its plaintext equivalent! We rush over to the nearby Wyse thin-client and try the password on the admin-mode login screen. Success!:


Can we improve it?

We are now aware of two crucial points:

  • We can now infer that a static key / algorithm is being used and that the plaintext can be trivially reversed
  • We can now decode passwords on demand, albeit via a slightly awkward process

So to neaten this and create a stand-alone solution, we need to work out the algorithm and there two obvious tactics left:

  1. Cryptanalysis
  2. Disassemble the configuration Generator

Option 2 is the logical first step. Specifically, we take a look at the bundled ‘CryptGui.exe’ binary again.
The application is written in C#, which is compiled into a byte code and run on the .NET virtual machine. This has the advantage for us that there is a strong correlation between the original code and the bytecode, i.e. we can decompile it (like we do for Java and Android applications).


That doesn’t look too helpful does it? Actually it is, but it’s not quite what we wanted. It basically is defining an external call to non-.NET code – it’s importing SysInfo.dll and then defining a direct mapping to a function within there, called NFuseDecode and which takes three parameters:

InBuffer – buffer of ciphertextlength – size of ciphertext

OutBuffer – buffer for plaintext

This means that we can’t just decompile the .NET code to work out the logic, we’d have to disassemble it and then trace through the instructions to work out what it is doing. So, we load the DLL into IDA Pro for analysis.

IDA graphically illustrates the system call flows as can be seen in the diagram below:


The diagram is zoomed out so we can’t see any mnemonics – just the flow, but we can see a few things:

  1. It’s a really short function
  2. It mainly consists of two loops, highlighted in red and blue

The two loops interested us; both of them required looking at separately, so here’s the first (red) one:

.text:10001AA0 loc_10001AA0: ; CODE XREF: NFuseDecode(x,x,x)+49j
.text:10001AA0 mov dl, [eax]
.text:10001AA2 sub dl, 1
.text:10001AA5 shl dl, 4
.text:10001AA8 add dl, [eax+1]
.text:10001AAB add eax, 2
.text:10001AAE sub dl, 41h
.text:10001AB1 mov [ecx], dl
.text:10001AB3 add ecx, 1
.text:10001AB6 sub esi, 1
.text:10001AB9 jnz short loc_10001AA0
.text:10001ABB pop esi

To those unfamiliar with assembly, this looks like a pile of random characters, but is actually quite simple. We have translated this into C to demonstrate what it’s doing:

for (int i=0; i < length; i++)
a = src[i];
a -= 1;
a <<= 4;
a += src[i+1];
a -= 0x41
dst[i] = a;

This is essentially a base26 decoder – i.e. it will translate two upper case characters into a byte of data. To try and walk this through, I’ll use the characters ME:

  1. Take the first character M, which is 0x4d in hex
  2. Take one from it (= 0x4c)
  3. Shift it left by 4 bits, this basically throws away the top nybble and moves the bottom nybble into the top nybble (= 0xc0)
  4. Take the second character E, which is 0x45 in hex and add it to our previous value (= 0x105)
  5. Take away 0x41, the hex value of A (= 0xc4)

So the first loop is just a binary to upper case convertor, let’s look at the second (blue loop):

.text:10001AD0 loc_10001AD0: ; CODE XREF: NFuseDecode(x,x,x)+76j
.text:10001AD0 mov dl, [eax-1]
.text:10001AD3 xor dl, [eax]
.text:10001AD5 add ecx, 0FFFFh
.text:10001ADB xor dl, 0A5h
.text:10001ADE mov [eax], dl
.text:10001AE0 sub eax, 1
.text:10001AE3 test cx, cx
.text:10001AE6 ja short loc_10001AD0

This is performing the encoding by working backwards through the decoded string and XORing each byte with the one before it and the static key of 0xa5, or in C:

for (i=length; i>=0; i–)

Working through this with the text for ‘bab’

  • Crypted password: MHADME
  • Base26 decode: 0xc7 0x03 0xc4
  • Third character = 0x03 XOR 0xc4 XOR 0xa5 = 0x62 (‘b’)
  • Second character = 0xc7 XOR 0x03 XOR 0xa5 = 0x61 (‘a’)
  • First character = 0x00 XOR 0xc7 XOR 0xa5 = 0x62 (‘b’)
  • Plaintext: bab

To save time we have mangled this into a simple C program, you can find the source on our github page at


Final thoughts

The fact that passwords are being stored in a reversible format isn’t great. It took us less than an hour to go from our ‘wnos.ini’ file to plaintext password, and we can now decode any password on demand with the provided script.
The password can be used on the Wyse thin-client to gain access to the admin panel:


This in itself doesn’t provide a great deal of advantage to an attacker / malicious user, but nonetheless it can be used to tamper with the local configuration of the Wyse thin-client and perform actions that might otherwise be blocked.

A more pressing concern is that of password re-use. We have seen clients reusing the password stored in the wnos.ini file for other internal systems! Using the methods outlined in this article, an attacker could potentially use the wnos.ini file to gain a foothold on an internal domain.

We cannot see an alternative to using the default encoding algorithm(s). The Wyse thin-clients simply do not seem to support any form of secure password storage, i.e. hashing.

At this stage, our main piece of advice would be that of common sense: Ensure that the password stored within the wnos.ini files are not used elsewhere!

We also advise that you check for the default BIOS password of ‘Fireport’ (case sensitive).

My colleague Dave Lodge (AKA @tautology0) has another method published here.