Skip to content

Commit

Permalink
init commit facefishconfig
Browse files Browse the repository at this point in the history
  • Loading branch information
selsocono committed Apr 12, 2024
1 parent d009e2b commit 68e3766
Show file tree
Hide file tree
Showing 7 changed files with 384 additions and 0 deletions.
92 changes: 92 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
FROM ubuntu:latest

LABEL about="This file is just container..."
LABEL version="3.0.1"
LABEL author="@selsocono"

ENV GO=1.22.2
ENV OPENSSL=openssl-3.3.0
ENV YARA_VERSION=v4.5.0
ENV HILLU=4.3.2

ENV GOLANGARCH=amd64
ENV BUILD_THREADS=8
ENV ARCH_x32=i686
ENV ARCH=x86_64
ENV GOOS=windows
ENV GOARCH=amd64
ENV MINGWARCH_x32=mingw
ENV MINGWARCH=mingw64
ENV WORKDIR=/home
ENV GODIR=/usr/local
ENV DEPSDIR=$WORKDIR/${ARCH}-w64-mingw32/deps
ENV DEPSDIR_x32=$WORKDIR/${ARCH_x32}-w64-mingw32/deps
ENV BUILDDIR=$WORKDIR/build
ENV CGO_ENABLED=1
ENV GOCACHE=$WORKDIR/build-cache/go
ENV GOMODCACHE=$WORKDIR/build-cache/mod

WORKDIR $WORKDIR

RUN mkdir -p $BUILDDIR && mkdir -p $DEPSDIR && mkdir -p $DEPSDIR_x32 && mkdir -p $GODIR && \
mkdir -p /root/go/src/github.com/hillu/go-yara && mkdir -p $GODIR/go/src/golang.org/ && mkdir -p $WORKDIR

#DEPENDENCES
RUN apt-get clean && apt-get update && apt-get install -y wget build-essential pkg-config git gcc-multilib \
gcc-mingw-w64 autoconf automake libtool libjansson-dev libmagic-dev libssl-dev flex zip

#OPENSSL x32
RUN git clone --depth=1 --branch=$OPENSSL \
https://github.com/openssl/openssl.git $WORKDIR/${ARCH_x32}-w64-mingw32/openssl
RUN cp -r $WORKDIR/${ARCH_x32}-w64-mingw32/openssl $WORKDIR/${ARCH}-w64-mingw32/openssl
WORKDIR $WORKDIR/${ARCH_x32}-w64-mingw32/openssl
RUN ./Configure --prefix=$DEPSDIR_x32 --cross-compile-prefix=${ARCH_x32}-w64-mingw32- \
shared $MINGWARCH_x32 && make -j$BUILD_THREADS && make install_dev

#YARA x32
WORKDIR $WORKDIR
RUN git clone --depth=1 --branch=$YARA_VERSION https://github.com/VirusTotal/yara.git $WORKDIR/${ARCH_x32}-w64-mingw32/yara
RUN cp -r $WORKDIR/${ARCH_x32}-w64-mingw32/yara $WORKDIR/${ARCH}-w64-mingw32/yara
WORKDIR $WORKDIR/${ARCH_x32}-w64-mingw32/yara
ENV PKG_CONFIG_LIBDIR=$DEPSDIR_x32/lib/pkgconfig
RUN ./bootstrap.sh
RUN ./configure CPPFLAGS="$(pkg-config --static --cflags openssl)" LDFLAGS="$(pkg-config --static --libs openssl)" \
--prefix=$DEPSDIR_x32 --host=${ARCH_x32}-w64-mingw32 --disable-shared --with-crypto --enable-dotnet && \
make -j$BUILD_THREADS && make install

#OPENSSL x64
WORKDIR $WORKDIR/${ARCH}-w64-mingw32/openssl
RUN ./Configure --prefix=$DEPSDIR --cross-compile-prefix=${ARCH}-w64-mingw32- shared $MINGWARCH && \
make -j$BUILD_THREADS && make install_dev

#YARA x64
WORKDIR $WORKDIR/${ARCH}-w64-mingw32/yara
ENV PKG_CONFIG_LIBDIR=$DEPSDIR/lib64/pkgconfig
RUN ./bootstrap.sh
RUN ./configure CPPFLAGS="$(pkg-config --static --cflags openssl)" LDFLAGS="$(pkg-config --static --libs openssl)" \
--prefix=$DEPSDIR --host=${ARCH}-w64-mingw32 --disable-shared --with-crypto --enable-dotnet && \
make -j$BUILD_THREADS && make install

#GOLANG
WORKDIR $WORKDIR
RUN wget https://golang.org/dl/go$GO.linux-$GOLANGARCH.tar.gz
RUN tar -xvf go$GO.linux-$GOLANGARCH.tar.gz -C $GODIR

#ENV GO111MODULE=off

#HILLU
WORKDIR $WORKDIR
RUN wget https://github.com/hillu/go-yara/archive/v$HILLU.tar.gz
RUN tar -xvf $WORKDIR/v$HILLU.tar.gz -C $WORKDIR/
WORKDIR $WORKDIR/go-yara-$HILLU
RUN cp -r ./ /root/go/src/github.com/hillu/go-yara

ENV PKG_CONFIG_LIBDIR=$DEPSDIR/lib64/pkgconfig
ENV PKG_CONFIG_PATH=$DEPSDIR/lib/pkgconfig
ENV CC=$ARCH-w64-mingw32-gcc
ENV LD=$ARCH-w64-mingw32-ld
ENV GOROOT=$GODIR/go
ENV GOPATH=/root/go
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$GODIR/go/bin"

WORKDIR $WORKDIR
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
The utility receives configuration data from the FaceFish rootkit, which is encrypted with the <code>[Blowfish](https://en.wikipedia.org/wiki/Blowfish_(cipher))
</code>algorithm.

The FaceFish rootkit is very popular in the wild. A detailed analysis of the rootkit in the following material<code>[Analysis report of the Facefish rootkit](https://blog.netlab.360.com/ssh_stealer_facefish_en/)
</code>and<code>[Linux Servers Hijacked to Implant SSH Backdoor](https://blogs.juniper.net/en-us/threat-research/linux-servers-hijacked-to-implant-ssh-backdoor)
</code>.

Examples:
```
PS D:\facefishconfig> .\facefishconfig.win64.exe --dir=C:\samples
FaceFish Dropper: C:\samples\ssh1200, 118128, 9d32e96874eec67975e3b1bd6f5a2dd550d7a3b82d5b7d47f82974750cb038ba
00000000 c3 fe dd 71 b0 04 00 00 20 00 00 00 39 05 00 00 |...q.... ...9...|
00000010 00 00 00 00 00 00 00 00 68 74 74 70 3a 2f 2f 31 |........http://1|
00000020 34 36 2e 31 39 30 2e 32 33 2e 38 36 2f 69 6e 64 |46.190.23.86/ind|
00000030 65 78 2e 70 68 70 00 00 00 00 00 00 00 00 00 00 |ex.php..........|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 |........|
FaceFish Rootkit: C:\samples\libs.so, 166160, 05ba963fa7a52c48f3a9b3e9de702b735ef5e30f2931a1f8d7342410ccada105
00000000 c3 fe dd 71 b0 04 00 00 20 00 00 00 39 05 00 00 |...q.... ...9...|
00000010 00 00 00 00 00 00 00 00 68 74 74 70 3a 2f 2f 31 |........http://1|
00000020 34 36 2e 31 39 30 2e 32 33 2e 38 36 2f 69 6e 64 |46.190.23.86/ind|
00000030 65 78 2e 70 68 70 00 00 00 00 00 00 00 00 00 00 |ex.php..........|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 |........|
FaceFish Dropper: C:\samples\ssh3600, 118128, c50bd9865ed65a9c298768f245d8eaff1baa410735ff5673a73d1411c425b7c6
00000000 cc 2c 88 83 10 0e 00 00 20 00 00 00 00 00 00 00 |.,...... .......|
00000010 00 00 00 00 00 00 00 00 68 74 74 70 3a 2f 2f 65 |........http://e|
00000020 75 2d 64 65 62 69 61 6e 2e 63 6f 6d 2f 69 6e 64 |u-debian.com/ind|
00000030 65 78 2e 70 68 70 00 00 00 00 00 00 00 00 00 00 |ex.php..........|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 |........|
FaceFish Dropper: C:\samples\ssh3600_, 118128, 740a3f10b45a607abaf0045108ee6ccb8f30d7439eadb3f06a00cf0026dfc1d8
00000000 9e b6 06 0a b0 04 00 00 20 00 00 00 00 00 00 00 |........ .......|
00000010 00 00 00 00 00 00 00 00 68 74 74 70 3a 2f 2f 73 |........http://s|
00000020 74 6f 6c 6f 74 6f 2e 61 69 2f 69 6e 64 65 78 2e |toloto.ai/index.|
00000030 70 68 70 00 00 00 00 00 00 00 00 00 00 00 00 00 |php.............|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 |........|
FaceFish Rootkit: C:\samples\libs.so, 166160, 1a3199d35e84df4598becf234b4ec39f3a30aabb7b6e1002f2016072554961b4
00000000 9e b6 06 0a b0 04 00 00 20 00 00 00 00 00 00 00 |........ .......|
00000010 00 00 00 00 00 00 00 00 68 74 74 70 3a 2f 2f 36 |........http://6|
00000020 34 2e 32 32 37 2e 31 32 34 2e 32 34 32 2f 6d 69 |4.227.124.242/mi|
00000030 72 72 6f 72 2f 00 00 00 00 00 00 00 00 00 00 00 |rror/...........|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 |........|
FaceFish Rootkit: C:\samples\libs.so_, 31048, 58c49dc1dc8c6bdb85985ae0918e9717045b9e80db5f4b1758ac5b20ad3230c7
00000000 00 00 00 00 0f 00 00 00 20 00 00 00 01 bb 00 00 |........ .......|
00000010 00 00 00 00 00 00 00 00 6c 69 62 2e 72 70 6d 2d |........lib.rpm-|
00000020 62 69 6e 2e 6c 69 6e 6b 00 00 00 00 00 00 00 00 |bin.link........|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 |........|
```
8 changes: 8 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
docker build -t facefish-config-container -f ./Dockerfile .
docker run --rm --volume "./:/root/go/src/facefishconfig/" -i facefish-config-container <<EOF
cd /root/go/src/facefishconfig/
go build -ldflags '-s -w -extldflags "-static"' -trimpath -buildvcs=false -o /root/go/src/facefishconfig/facefishconfig.win64.exe -tags yara_static -buildmode=exe
zip -r facefishconfig.zip facefishconfig.win64.exe rules.yar
EOF
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module facefishconfig

go 1.22.2

require (
github.com/daviddengcn/go-colortext v1.0.0
github.com/hillu/go-yara/v4 v4.3.2
golang.org/x/crypto v0.22.0
)
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE=
github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
github.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A=
github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE=
github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrSEUQ=
github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA=
github.com/hillu/go-yara/v4 v4.3.2 h1:HGqUN3ORUduWZbb95RQjut4UzavGDbtt/C6SnGB3Amk=
github.com/hillu/go-yara/v4 v4.3.2/go.mod h1:AHEs/FXVMQKVVlT6iG9d+q1BRr0gq0WoAWZQaZ0gS7s=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
157 changes: 157 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package main

import (
"bufio"
"bytes"
"crypto/cipher"
"crypto/sha256"
"encoding/hex"
"flag"
"fmt"
ct "github.com/daviddengcn/go-colortext"
"github.com/hillu/go-yara/v4"
"golang.org/x/crypto/blowfish"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
)

func main() {
dir := flag.String("dir", "", `path to scan file or directory. example: C:\Windows\System32\`)
rules := flag.String("rules", "rules.yar", `path to file with rules. example: C:\test1.yar`)
flag.Parse()

file, err := ioutil.ReadFile(*rules)
if err != nil {
log.Println(err)
return
}

c, err := yara.NewCompiler()
if c == nil || err != nil {
log.Println(err)
return
}

if err = c.AddString(string(file), ""); err != nil {
log.Println(err)
return
}

r, err := c.GetRules()
if err != nil {
log.Println(err)
return
}

err = filepath.Walk(*dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Println(err)
return nil
}

if info.IsDir() {
return nil
}

file, err := os.Open(path)
if err != nil {
//log.Println(err)
return nil
}

defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Println(err)
}
}(file)

var matches yara.MatchRules

err = r.ScanFileDescriptor(file.Fd(), 0, 0, &matches)
if err != nil {
log.Println(err)
}

if len(matches) < 0 {
return nil
}

var ffRootkit, ffDropper, ffConfig bool
var ffConfigOffset uint64

_, err = file.Seek(0, 0)
if err != nil {
return err
}

for _, match := range matches {
if match.Rule == "FaceFish_Rootkit" {
ffRootkit = true
}
if match.Rule == "FaceFish_Config" {
ffConfigOffset = match.Strings[0].Offset
ffConfig = true
}
if match.Rule == "FaceFish_UPX_overlay_trick" {
ffDropper = true
}
}

buf := new(bytes.Buffer)
_, err = buf.ReadFrom(file)
if err != nil {
return err
}

_, err = file.Seek(0, 0)
if err != nil {
return err
}

SHA256 := sha256.New()

pagesize := os.Getpagesize()
reader := bufio.NewReaderSize(file, pagesize)
multiWriter := io.MultiWriter(SHA256)

_, err = io.Copy(multiWriter, reader)
if err != nil {
log.Println(err)
}

if ffRootkit && ffConfig {
ct.Foreground(ct.Yellow, true)
fmt.Printf("FaceFish Rootkit: %s, %d, %s\n", file.Name(), info.Size(), hex.EncodeToString(SHA256.Sum(nil)))
ct.ResetColor()
fmt.Println(hex.Dump(blowfishDecrypt(buf.Bytes()[ffConfigOffset-128:ffConfigOffset],
buf.Bytes()[ffConfigOffset-144:ffConfigOffset-128])))
}

if ffDropper {
ct.Foreground(ct.Red, true)
fmt.Printf("FaceFish Dropper: %s, %d, %s\n", file.Name(), info.Size(), hex.EncodeToString(SHA256.Sum(nil)))
ct.ResetColor()
fmt.Println(hex.Dump(blowfishDecrypt(buf.Bytes()[len(buf.Bytes())-128:], []byte("buil"))))
}
return nil
})
}

func blowfishDecrypt(et, key []byte) []byte {
dcipher, err := blowfish.NewCipher(key)
if err != nil {
panic(err)
}
div := et[:blowfish.BlockSize]
decrypted := et[blowfish.BlockSize:]
if len(decrypted)%blowfish.BlockSize != 0 {
panic("decrypted is not a multiple of blowfish.BlockSize")
}
dcbc := cipher.NewCBCDecrypter(dcipher, div)
dcbc.CryptBlocks(decrypted, decrypted)
return decrypted
}
36 changes: 36 additions & 0 deletions rules.yar
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
rule FaceFish_UPX_overlay_trick {
strings:
$sig = {00 00 00 00 55 50 58 21 00 00 00 00 00 00 55 50 58 21}
condition:
uint32be ( 0 ) == 0x7F454C46 and all of ( $sig* ) and for any i in ( 150 .. 180 ) : ( uint8be ( @sig + i ) )
}

rule FaceFish_Config {
strings:
$a1 = {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 84 80 84 80 84 80 84 80 88 80 88 80 88 80 88}
condition:
uint32be ( 0 ) == 0x7F454C46 and all of them
}

rule FaceFish_Rootkit{
strings:
$export_init = "sshinit" ascii
$export_bind = "bind" ascii
$remoteShellCmd = "/bin/sh" ascii
$remoteShellOpt = "-i" ascii
$interceptedFunc1 = "sshpam_auth_passwd" ascii
$interceptedFunc2 = "auth_shadow_pwexpired" ascii
$interceptedFunc3 = "getpwnamallow" ascii
$interceptedFunc4 = "do_log" ascii
$interceptedFunc5 = "login_write" ascii
$interceptedFunc6 = "read_passphrase" ascii
$interceptedFunc7 = "ssh_userauth2" ascii
$interceptedFunc8 = "key_perm_ok" ascii
$interceptedFunc9 = "load_identity_file" ascii
$loadCommandCode1 = {BF 00 03 00 00}
$loadCommandCode2 = {BF 01 03 00 00}
$loadCommandCode3 = {BF 05 03 00 00}
$loadCommandCode4 = {BF 11 03 00 00}
condition:
uint32be ( 0 ) == 0x7F454C46 and filesize < 300000 and ( all of ( $export* ) ) and ( all of ( $remoteShel* ) ) and ( any of ( $interceptedFunc* ) ) and ( 2 of ( $loadCommandCode* ) )
}

0 comments on commit 68e3766

Please sign in to comment.