-0x01 - Pendahuluan
Artikel ini dikhususkan untuk analisis semua tugas Flare-On 2019 - kompetisi tahunan untuk rekayasa balik dari FireEye. Dalam kompetisi ini, saya ambil bagian untuk kedua kalinya. Pada tahun sebelumnya, saya berhasil mencapai tempat ke-11 dalam hal waktu penyelesaian, setelah menyelesaikan semua masalah dalam waktu sekitar 13 hari. Tahun ini, serangkaian tugas lebih mudah, dan saya bertemu dalam 54 jam, mengambil pada waktu yang sama 3 tempat dalam hal pengiriman.
Dalam artikel ini saya mencoba menggambarkan momen-momen yang membangkitkan minat terbesar saya, oleh karena itu, analisis tidak akan menggambarkan rutinitas bekerja di IDA, memahami algoritme masing-masing fungsi dan hal-hal lain yang tidak terlalu menarik. Saya harap setelah membaca ini, Anda akan menemukan sesuatu yang baru dan berguna untuk diri Anda sendiri. Anda dapat menemukan analisis masalah dari penulis, serta beberapa statistik dan hadiah untuk para pemenang di sini .
Jika Anda tertarik, selamat datang di kucing!
0x00 - Konten
- 0x01 - Battlestation Memecat [Edisi Demo Shareware]
- 0x02 - Terlalu panjang
- 0x03 - Flarebear
- 0x04 - Dnschess
- 0x05 - demo
- 0x06 - bmphide
- 0x07 - wopr
- 0x08 - ular
- 0x09 - dimuat ulang
- 0x0A - Mugatu
- 0x0B - vv_max
- 0x0C - bantuan
- 0x0D - Ringkasan
0x01 - Battlestation Memecat [Edisi Demo Shareware]
Selamat datang di Six Flare-On Challenge!
Ini adalah permainan sederhana. Reverse engineer-nya untuk mencari tahu "kode senjata" apa yang perlu Anda masukkan untuk mengalahkan masing-masing dari dua musuh dan layar kemenangan akan mengungkapkan bendera. Masukkan bendera di sini di situs ini untuk mencetak gol dan beralih ke level berikutnya.
* Tantangan ini ditulis dalam .NET. Jika Anda belum memiliki alat .NET reverse engineering favorit, saya sarankan dnSpy
** Jika Anda sudah menyelesaikan versi lengkap game ini di gerai kami di BlackHat atau rilis berikutnya di twitter, selamat, masukkan bendera dari layar kemenangan sekarang untuk melewati level ini.
Tugas ini disusun sebelumnya sebagai bagian dari Black Hat USA 2019, sekitar waktu yang sama ketika saya memutuskannya. Saya tidak ingat bagaimana dia menyelesaikannya Tugasnya cukup sederhana, jadi kami tidak akan mempertimbangkan solusinya.
0x02 - Terlalu panjang
Rahasia dari tantangan berikut ini disembunyikan dengan cerdik. Namun, dengan pendekatan yang tepat, menemukan solusi tidak akan memakan waktu terlalu lama.
Diberikan file .exe x86. Ketika Anda mencoba memulai, sebuah pesan ditampilkan dengan konten berikut:

Saat menganalisis aplikasi, Anda mungkin menemukan bahwa pesan disimpan dalam beberapa penyandian dengan panjang karakter variabel (dari 1 hingga 4 byte). Ketika fungsi decoding dipanggil, ia menerima panjang hasil yang diharapkan, yang lebih pendek dari pesan itu sendiri, itulah sebabnya bendera tidak terlihat. Anda dapat memperbaiki nilai panjang yang diteruskan ke fungsi dalam mode debug dan mendapatkan pesan lengkap dengan bendera:

Anda juga bisa menulis ulang algoritma penguraian kode dengan Python dan mendapatkan tanda:
msg = [ ... ]
0x03 - Flarebear
Kami di Flare telah menciptakan hewan peliharaan Tamagotchi kami sendiri, flarebear. Dia sangat cerewet. Buat dia tetap hidup dan bahagia dan dia akan memberi Anda bendera.
Dalam tugas ini, file apk
untuk Android
. Pertimbangkan metode solusi tanpa memulai aplikasi itu sendiri.
Langkah pertama adalah mendapatkan kode sumber aplikasi. Untuk melakukan ini, menggunakan dex2jar
utilitas dex2jar
konversikan apk
ke jar
dan kemudian dapatkan kode sumber Java
menggunakan dekompiler, yang saya lebih suka menggunakan cfr
.
~/retools/d2j/d2j-dex2jar.sh flarebear.apk java -jar ~/retools/cfr/cfr-0.146.jar --outputdir src flarebear-dex2jar.jar
Dengan menganalisis kode sumber aplikasi, Anda dapat menemukan metode .danceWithFlag()
yang menarik, yang terletak di file FlareBearActivity.java
. Di dalam .danceWithFlag()
, sumber daya aplikasi raw
didekripsi menggunakan metode .decrypt(String, byte[])
, argumen pertama di antaranya adalah string yang diperoleh dengan menggunakan metode .getPassword()
. Tentunya benderanya adalah sumber daya terenkripsi, jadi mari kita coba mendekripsi mereka. Untuk melakukan ini, saya memutuskan untuk menulis ulang sedikit kode yang telah dikompilasi, menyingkirkan dependensi Android
dan hanya menyisakan metode yang diperlukan untuk dekripsi, sehingga sebagai hasilnya, kode yang dihasilkan dapat dikompilasi. Lebih lanjut, selama analisis, ditemukan bahwa metode .getPassword()
bergantung pada tiga nilai status integer. Setiap nilai terletak pada interval kecil dari 0
hingga N
, sehingga Anda dapat menelusuri semua nilai yang mungkin untuk mencari kata sandi yang diinginkan.
Hasilnya adalah kode berikut:
Main.java import java.io.InputStream; import java.nio.charset.Charset; import java.security.Key; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.KeySpec; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; import java.util.Collections; import java.io.*; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public final class Main { public static void main (String args []) throws Exception { Main a = new Main(); InputStream inputStream = new FileInputStream("ecstatic"); long fileSize = new File("ecstatic").length(); byte[] file1 = new byte[(int) fileSize]; inputStream.read(file1); inputStream = new FileInputStream("ecstatic2"); fileSize = new File("ecstatic2").length(); byte[] file2 = new byte[(int) fileSize]; inputStream.read(file2); for(int i = 0; i < 9; i++) { for(int j = 0; j < 7; j++) { for(int k = 1; k < 16; k++) { String pass = a.getPassword(i, j, k); try { byte[] out1 = a.decrypt(pass, file1); byte[] out2 = a.decrypt(pass, file2); OutputStream outputStream = new FileOutputStream("out1"); outputStream.write(out1); outputStream = new FileOutputStream("out2"); outputStream.write(out2); System.out.println("yep!"); } catch (javax.crypto.BadPaddingException ex) { } } } } } public final byte[] decrypt(Object object, byte[] arrby) throws Exception { Object object2 = Charset.forName("UTF-8"); object2 = "pawsitive_vibes!".getBytes((Charset)object2); object2 = new IvParameterSpec((byte[])object2); object = ((String)object).toCharArray(); Object object3 = Charset.forName("UTF-8"); object3 = "NaClNaClNaCl".getBytes((Charset)object3); object = new PBEKeySpec((char[])object, (byte[])object3, 1234, 256); object = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret((KeySpec)object); object3 = new SecretKeySpec(((SecretKey)object).getEncoded(), "AES"); object = Cipher.getInstance("AES/CBC/PKCS5Padding"); ((Cipher)object).init(2, (Key)object3, (AlgorithmParameterSpec)object2); object = ((Cipher)object).doFinal(arrby); return (byte [])object; } public final String getPassword(int n, int n2, int n3) { String string2 = "*"; String string3 = "*"; switch (n % 9) { case 8: { string2 = "*"; break; } case 7: { string2 = "&"; break; } case 6: { string2 = "@"; break; } case 5: { string2 = "#"; break; } case 4: { string2 = "!"; break; } case 3: { string2 = "+"; break; } case 2: { string2 = "$"; break; } case 1: { string2 = "-"; break; } case 0: { string2 = "_"; } } switch (n3 % 7) { case 6: { string3 = "@"; break; } case 4: { string3 = "&"; break; } case 3: { string3 = "#"; break; } case 2: { string3 = "+"; break; } case 1: { string3 = "_"; break; } case 0: { string3 = "$"; } case 5: } String string4 = String.join("", Collections.nCopies(n / n3, "flare")); String string5 = String.join("", Collections.nCopies(n2 * 2, this.rotN("bear", n * n2))); String string6 = String.join("", Collections.nCopies(n3, "yeah")); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(string4); stringBuilder.append(string2); stringBuilder.append(string5); stringBuilder.append(string3); stringBuilder.append(string6); return stringBuilder.toString(); } public final String rotN(String charSequence, int n) { Collection<String> collection = new ArrayList(charSequence.length()); for (int i = 0; i < charSequence.length(); ++i) { char c; char c2 = c = charSequence.charAt(i); if (Character.isLowerCase(c)) { char c3; c2 = c3 = (char)(c + n); if (c3 > 'z') { c2 = c3 = (char)(c3 - n * 2); } } collection.add(Character.valueOf(c2).toString()); } return collection.stream().collect(Collectors.joining());
Kami akan mengekstrak sumber daya terenkripsi, kompilasi, dan menjalankan file yang dihasilkan:
$ ~/retools/apktool/apktool d flarebear.apk $ cp flarebear/res/raw/* . $ javac Main.java $ java Main
Untungnya, hanya satu dari semua opsi kata sandi yang Anda pilih yang cocok. Hasilnya, kami mendapatkan dua gambar dengan bendera:
~/flareon2019/3 - Flarebear$ file out* out1: PNG image data, 2100 x 2310, 8-bit/color RGB, non-interlaced out2: PNG image data, 2100 x 2310, 8-bit/color RGB, non-interlaced


0x04 - Dnschess
Beberapa lalu lintas jaringan yang mencurigakan mengarahkan kami ke program catur tanpa izin ini yang berjalan di desktop Ubuntu. Tampaknya ini adalah karya peretas komputer dunia maya. Anda harus melakukan langkah yang benar untuk menyelesaikannya. Semoga beruntung
Tugas ini berisi dump lalu lintas, file executable ELF
ChessUI
dan library ChessAI.so
. Dengan menjalankan file yang dapat dieksekusi, Anda dapat melihat papan catur.

Mari kita mulai analisis dengan dump lalu lintas.

Semua lalu lintas terdiri dari kueri ke server DNS
Tipe A
Pertanyaan itu sendiri terdiri dari nama-nama potongan, deskripsi langkah dalam permainan catur dan bagian konstan dari .game-of-thrones.flare-on.com
, misalnya rook-c3-c6.game-of-thrones.flare-on.com
. Secara konstan, Anda dapat dengan mudah menemukan tempat yang tepat di perpustakaan ChessAI.so
:
signed __int64 __fastcall getNextMove(int idx, const char *chess_name, unsigned int pos_from, unsigned int pos_to, \__int64 a5) { struct hostent *v9;
Dapat dilihat dari kode itu, berdasarkan alamat ip
diterima, string byte tertentu didekripsi, yang disimpan di area memori lain, yang saya sebut flag
.
Untuk menyelesaikan tugas tersebut, hal pertama yang harus dilakukan adalah mendapatkan semua alamat ip
dari dump lalu lintas. Anda dapat melakukan ini dengan perintah berikut:
tshark -r capture.pcap | grep -P -o '127.(\d+).(\d+).(\d+)' | grep -v '127.0.0.1'
ips
menyimpan semua alamat ip
ke file ips
, ips
dapat menggunakan kode Python
berikut untuk mendapatkan benderanya:
with open('ips') as f: ips = f.read().split() flag = bytearray(64) key = b'yZ\xb8\xbc\xec\xd3\xdf\xdd\x99\xa5\xb6\xac\x156\x85\x8d\t\x08wRMqT}\xa7\xa7\x08\x16\xfd\xd7' for ip in ips: a, b, c, d = map(int, ip.split('.')) if d & 1: continue idx = c & 0xf if idx > 14: continue flag[2*idx] = b ^ key[2*idx] flag[2*idx + 1] = b ^ key[2*idx + 1] print(flag.decode() + '@flare-on.com')
0x05 - demo
Seseorang di tim Flare mencoba membuat kami terkesan dengan keterampilan demoscene mereka. Tampaknya kosong. Lihat apakah Anda dapat mengetahuinya atau mungkin kami harus memecat mereka. Tidak ada tekanan.
Diberikan file executable 4k.exe
, yang menggunakan DirectX
. Saat diluncurkan, logo FlareOn
berputar ditampilkan di jendela utama.

Analisis statis program mengungkapkan satu fungsi, yang merupakan titik masuk. Dalam konten, fungsinya menyerupai implementasi dekripsi kode. Kami tidak akan membuang waktu menganalisis algoritma dari fungsi ini, cukup berikan breakpoint pada instruksi ret
dan lihat di mana kontrol ditransfer. Setelah kembali, kami menemukan 0x00420000
di alamat 0x00420000
, kode di mana dibongkar sebagai sesuatu yang memadai:

Kemudian diputuskan untuk mentransfer kode ini dari mode debug ke database IDA
menggunakan API
dan melanjutkan analisis statis.
Kode baru di awal mengimpor fungsi yang diperlukan dari berbagai perpustakaan. Tabel fungsi-fungsi ini juga dapat dipulihkan dalam dinamika. Hasilnya adalah serangkaian fungsi berikut:

Titik masuk "nyata" ke program adalah:

Perhatikan pembuatan DeviceInterface
tipe IDirect3DDevice9 **
. Di masa depan, antarmuka ini digunakan secara aktif, dan untuk menyederhanakan kebalikannya, perlu untuk menentukan tabel metode-metodenya. Dimungkinkan untuk dengan cepat menemukan definisi antarmuka, misalnya, di sini . Saya mem-parsing tabel ini dan mengubahnya menjadi struktur untuk IDA
. Menerapkan jenis yang dihasilkan ke DeviceInterface
dapat secara signifikan menyederhanakan analisis kode lebih lanjut. Tangkapan layar berikut menunjukkan hasil dekompiler untuk fungsi utama siklus rendering adegan sebelum dan sesudah menerapkan tipe.


Setelah analisis lebih lanjut, ditemukan bahwa dua jerat poligon (mesh, poligon mesh) dibuat dalam program, meskipun ketika program berjalan kita hanya melihat satu objek. Juga, ketika membangun kisi-kisi, simpul mereka dienkripsi menggunakan XOR
, yang juga menimbulkan kecurigaan. Mari menguraikan dan memvisualisasikan simpul. Kotak kedua paling menarik, karena ini memiliki lebih banyak simpul. matplotlib
semua simpul, saya menemukan bahwa koordinat Z
masing-masing adalah 0, jadi untuk visualisasi diputuskan untuk menggambar grafik dua dimensi menggunakan matplotlib
. Kode berikut dan hasilnya dengan bendera ternyata:
import struct import matplotlib.pyplot as plt with open('vertexes', 'rb') as f: data = f.read() n = len(data) // 4 data = list(struct.unpack('{}I'.format(n), data)) key = [0xCB343C8, 0x867B81F0, 0x84AF72C3] data = [data[i] ^ key[i % 3] for i in range(len(data))] data = struct.pack('{}I'.format(n), *data) data = list(struct.unpack('{}f'.format(n), data)) x = data[0::3] y = data[1::3] z = data[2::3] print(z) plt.plot(x, y) plt.show()

0x06 - bmphide
Tyler Dean mendaki Gunung. Elbert (gunung tertinggi di Colorado) pada jam 2 pagi untuk mengambil gambar ini pada waktu yang tepat. Jangan pernah melewatkan hari leg. Kami menemukan gambar ini dan dapat dieksekusi pada thumb drive yang ditinggalkannya di trail trail. Bisakah dia dipercaya?
Dalam tugas, file executable bmphide.exe
dan image image.bmp
. Dapat diasumsikan bahwa beberapa pesan disembunyikan dalam gambar menggunakan metode steganografi.
Biner ditulis dalam C#
, jadi saya menggunakan utilitas dnSpy
untuk analisis. Anda dapat segera melihat bahwa sebagian besar nama metode dikaburkan. Jika Anda melihat metode Program.Main
. Utama, Anda dapat memahami logika program dan membuat asumsi tentang tujuan beberapa di antaranya:
- Aplikasi diinisialisasi menggunakan metode
Program.Init()
- Baca file data dan file gambar
- Menggunakan metode
byte [] Program.h(byte [])
, beberapa konversi data dilakukan - Menggunakan metode
Program.i(Bitmap, byte[])
, data yang dikonversi dimasukkan ke dalam gambar - Gambar yang dihasilkan disimpan dengan nama baru.
Selama inisialisasi aplikasi, berbagai metode kelas A
dipanggil. Analisis superfisial dari kelas menunjukkan kesamaan beberapa metodenya dengan metode obfuscator ConfuserEx
(file AntiTamper.JIT.cs
). Aplikasi ini sangat terlindungi dari debugging. Pada saat yang sama, tidak mungkin untuk menghapus mekanisme perlindungan menggunakan utilitas de4dot
dan fork-nya, sehingga diputuskan untuk melanjutkan analisis.
Pertimbangkan metode Program.i
, yang digunakan untuk memasukkan data ke dalam gambar.
public static void i(Bitmap bm, byte[] data) { int num = Program.j(103); for (int i = Program.j(103); i < bm.Width; i++) { for (int j = Program.j(103); j < bm.Height; j++) { bool flag = num > data.Length - Program.j(231); if (flag) { break; } Color pixel = bm.GetPixel(i, j); int red = ((int)pixel.R & Program.j(27)) | ((int)data[num] & Program.j(228)); int green = ((int)pixel.G & Program.j(27)) | (data[num] >> Program.j(230) & Program.j(228)); int blue = ((int)pixel.B & Program.j(25)) | (data[num] >> Program.j(100) & Program.j(230)); Color color = Color.FromArgb(Program.j(103), red, green, blue); bm.SetPixel(i, j, color); num += Program.j(231); } } }
Sangat mirip dengan LSB
klasik, namun, di tempat-tempat di mana konstanta diharapkan, metode int Program.j(byte)
digunakan. Hasil kerjanya tergantung pada berbagai nilai global yang diperoleh, termasuk selama inisialisasi dalam metode Program.Init()
. Diputuskan untuk tidak membalikkan pekerjaannya, tetapi untuk mendapatkan semua nilai yang mungkin saat runtime. dnSpy
memungkinkan dnSpy
untuk mengedit kode aplikasi yang dnSpy
dan menyimpan modul yang dimodifikasi. Kami mengambil keuntungan dari ini dan menulis ulang metode Program.Main
. Utama sebagai berikut:
private static void Main(string[] args) { Program.Init(); Program.yy += 18; for (int i = 0; i < 256; i++) { Console.WriteLine(string.Format("j({0}) = {1}", i, Program.j((byte)i))); } }
Saat startup, kami mendapatkan nilai-nilai berikut:
E:\>bmphide_j.exe j(0) = 206 j(1) = 204 j(2) = 202 j(3) = 200 j(4) = 198 j(5) = 196 j(6) = 194 j(7) = 192 j(8) = 222 j(9) = 220 j(10) = 218 j(11) = 216 j(12) = 214 j(13) = 212 j(14) = 210 j(15) = 208 j(16) = 238 j(17) = 236 j(18) = 234 j(19) = 232 j(20) = 230 ...
Ganti panggilan ke Program.j
dalam metode Program.j
dengan konstanta yang dihasilkan:
public static void i(Bitmap bm, byte[] data) { int num = 0; for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { bool flag = num > data.Length - 1; if (flag) { break; } Color pixel = bm.GetPixel(i, j); int red = ((int)pixel.R & 0xf8) | ((int)data[num] & 0x7); int green = ((int)pixel.G & 0xf8) | (data[num] >> 3 & 0x7); int blue = ((int)pixel.B & 0xfc) | (data[num] >> 6 & 0x3); Color color = Color.FromArgb(0, red, green, blue); bm.SetPixel(i, j, color); num += 1; } } }
Sekarang menjadi jelas bagaimana cara memasukkan setiap byte pesan ke dalam gambar:
- bit 0 hingga 2 ditempatkan dalam 3 LSB pada saluran merah titik
- bit 3 hingga 5 ditempatkan di 3 LSB pada saluran hijau titik
- bit 6 hingga 7 ditempatkan di 2 LSBs dari saluran biru titik
Selanjutnya, saya mencoba mengulangi algoritma metode konversi data, tetapi hasil perhitungan tidak sesuai dengan output program. Ternyata, kelas A
juga memiliki fungsi untuk mengganti metode (dalam A.VerifySignature(MethodInfo m1, MethodInfo m2)
) dan memodifikasi IL
dari kode byte metode (dalam A.IncrementMaxStack
).
Untuk memilih metode yang perlu diganti dalam Program
, di Program.Init
, IL
bytecode IL
metode hash dan dibandingkan dengan nilai yang telah dihitung. Secara total, dua metode diganti. Untuk mengetahui yang mana, kami akan meluncurkan aplikasi di bawah debugger dengan menetapkan breakpoint pada A.VerifySignature
panggilan, dan Anda harus melewati panggilan A.CalculateStack()
di Program.Init
, karena Ini mencegah debugging.

Sebagai hasilnya, Anda dapat melihat bahwa metode Program.a
digantikan oleh Program.b
, dan Program.c
digantikan oleh Program.d
.
Sekarang Anda perlu berurusan dengan modifikasi bytecode:
private unsafe static uint IncrementMaxStack(IntPtr self, A.ICorJitInfo* comp, A.CORINFO_METHOD_INFO* info, uint flags, byte** nativeEntry, uint* nativeSizeOfCode) { bool flag = info != null; if (flag) { MethodBase methodBase = Ac(info->ftn); bool flag2 = methodBase != null; if (flag2) { bool flag3 = methodBase.MetadataToken == 100663317; if (flag3) { uint flNewProtect; A.VirtualProtect((IntPtr)((void*)info->ILCode), info->ILCodeSize, 4u, out flNewProtect); Marshal.WriteByte((IntPtr)((void*)info->ILCode), 23, 20); Marshal.WriteByte((IntPtr)((void*)info->ILCode), 62, 20); A.VirtualProtect((IntPtr)((void*)info->ILCode), info->ILCodeSize, flNewProtect, out flNewProtect); } else { bool flag4 = methodBase.MetadataToken == 100663316; if (flag4) { uint flNewProtect2; A.VirtualProtect((IntPtr)((void*)info->ILCode), info->ILCodeSize, 4u, out flNewProtect2); Marshal.WriteInt32((IntPtr)((void*)info->ILCode), 6, 309030853); Marshal.WriteInt32((IntPtr)((void*)info->ILCode), 18, 209897853); A.VirtualProtect((IntPtr)((void*)info->ILCode), info->ILCodeSize, flNewProtect2, out flNewProtect2); } } } } return A.originalDelegate(self, comp, info, flags, nativeEntry, nativeSizeOfCode); }
Jelas bahwa metode dengan nilai MetadataToken
tertentu, yaitu 0x6000015
dan 0x6000014
, akan dimodifikasi. Token ini sesuai dengan metode Program.h
dan Program.g
. dnSpy
memiliki built-in hex
editor, di mana, ketika melayang, data metode disorot: judulnya (disorot dalam warna ungu) dan kode byte (disorot dalam warna merah), seperti yang ditunjukkan pada tangkapan layar. Anda dapat pergi ke metode yang diinginkan di hex
editor dengan mengklik alamat yang sesuai di komentar sebelum metode yang didekompilasi (misalnya, File Offset: 0x00002924
).

Mari kita coba menerapkan semua modifikasi yang dijelaskan: kita akan membuat salinan file, dalam hex editor apa pun kita akan mengubah nilai pada offset yang diperlukan, yang kita pelajari dari dnSpy
dan kita akan mengganti metode a -> b
dan c -> d
di Program.h
. Kami juga menghapus dari Program.Init
semua panggilan ke modul A
Jika semuanya dilakukan dengan benar, maka ketika kita mencoba memasukkan beberapa pesan ke dalam gambar menggunakan aplikasi yang dimodifikasi, kita akan mendapatkan hasil yang sama seperti ketika aplikasi asli bekerja. Tangkapan layar di bawah ini menunjukkan kode yang telah dikompilasi dari metode aplikasi asli dan yang dimodifikasi.


Tetap membuat algoritma transformasi terbalik. Ini cukup sederhana, jadi saya hanya akan memberikan skrip Python
dihasilkan:
from PIL import Image
Dengan menjalankan skrip ini, kami mendapatkan gambar bmp
lain tanpa bendera. Mengulangi prosedur di atasnya, kita mendapatkan gambar terakhir dengan bendera.

0x07 - wopr
Kami menggunakan keterampilan peretasan komputer kami sendiri untuk "menemukan" AI ini di superkomputer militer. Itu sangat menyerupai film klasik 1983 WarGames. Mungkin hidup meniru seni? Jika Anda dapat menemukan kode peluncuran untuk kami, kami akan membiarkan Anda lolos ke tantangan berikutnya. Kami berjanji tidak akan memulai perang termonuklir.
Dalam tugasnya, aplikasi konsol worp.exe
. Tampaknya, untuk menyelesaikannya, Anda perlu mengambil beberapa kode.

Analisis titik masuk menunjukkan bahwa ini adalah arsip yang mengekstraksi sendiri. Saat memulai, variabel lingkungan _MEIPASS2
. Jika variabel ini tidak ada, direktori sementara dibuat ke mana isi arsip dibongkar, dan aplikasi dimulai sekali lagi dengan variabel lingkungan yang diberikan _MEIPASS2
. Konten Arsip:
. ├── api-ms-win-core-console-l1-1-0.dll ├── ... ├── ... ├── api-ms-win-crt-utility-l1-1-0.dll ├── base_library.zip ├── _bz2.pyd ├── _ctypes.pyd ├── _hashlib.pyd ├── libcrypto-1_1.dll ├── libssl-1_1.dll ├── _lzma.pyd ├── pyexpat.pyd ├── python37.dll ├── select.pyd ├── _socket.pyd ├── _ssl.pyd ├── this │ ├── __init__.py │ └── key ├── ucrtbase.dll ├── unicodedata.pyd ├── VCRUNTIME140.dll └── wopr.exe.manifest 1 directory, 56 files
Dilihat oleh konten, kita berurusan dengan aplikasi Python
dikemas dalam exe
. Untuk mendukung ini, dalam biner utama Anda dapat menemukan impor dinamis dari fungsi yang sesuai dari perpustakaan Python
: PyMarshal_ReadObjectFromString
, PyEval_EvalCode
dan lainnya. Untuk analisis lebih lanjut, Anda perlu mengekstrak bytecode Python
. Untuk melakukan ini, simpan konten arsip dari direktori sementara dan _MEIPASS2
variabel lingkungan _MEIPASS2
. Jalankan biner utama dalam mode debug dengan menetapkan breakpoint pada fungsi PyMarshal_ReadObjectFromString
. Fungsi ini mengambil sebagai argumen penunjuk ke buffer dengan kode Python
serial dan panjangnya. Kami membuang konten buffer yang diketahui panjang untuk setiap panggilan. Saya hanya mendapat 2 panggilan, sedangkan yang kedua, objek bersambung jauh lebih besar, dan kami akan menganalisisnya.
Cara yang cukup sederhana untuk menganalisis data yang diterima adalah mengonversinya ke format file .pyc
(bytecode Python
dikompilasi) dan mendekompilasi menggunakan uncompyle6
. Untuk melakukan ini, cukup menambahkan header 16 byte ke data yang diterima. Akibatnya, saya mendapat file berikut:
00000000: 42 0d 0d 0a 00 00 00 00 de cd 57 5d 00 00 00 00 B.........W].... 00000010: e3 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 ................ 00000020: 00 40 00 00 00 73 3c 01 00 00 64 00 5a 00 64 01 .@...s<...dZd 00000030: 64 02 6c 01 5a 01 64 01 64 02 6c 02 5a 02 64 01 dlZddlZd
Selanjutnya, kita mendekompilasi file yang dihasilkan menggunakan uncompyle6
:
uncompyle6 task.pyc > task.py
Jika kami mencoba menjalankan file yang didekompilasi, kami akan mendapatkan pengecualian di baris BOUNCE = pkgutil.get_data('this', 'key')
. Ini mudah diperbaiki dengan hanya menetapkan konten file key
dari arsip ke variabel BOUNCE
. Menjalankan ulang skrip, kita hanya akan melihat tulisan LOADING...
Rupanya, beberapa teknik digunakan dalam tugas yang mencegah dekompilasi. Kami melanjutkan untuk menganalisis kode Python
dihasilkan. Pada akhirnya, kita melihat siklus berikut:
for i in range(256): try: print(lzma.decompress(fire(eye(__doc__.encode()), bytes([i]) + BOUNCE))) except Exception: pass
Anda dapat memahami bahwa fungsi print
sebenarnya ditimpa sebagai exec
, dan argumennya hanya bergantung pada __doc__.encode()
- teks di awal file. print
print
try-except
. . , __doc__
. __doc__
:
import marshal with open('pycode1', 'rb') as inp: data = inp.read() code = marshal.loads(data) doc = code.co_consts[0] with open('doc.txt', 'w') as outp: outp.write(doc)
, __doc__
. , i
, . . wrong
:
trust = windll.kernel32.GetModuleHandleW(None)
, . 0x100000
wrong
, . , .
. z3
:
from z3 import * from stage2 import wrong xor = [212, 162, 242, 218, 101, 109, 50, 31, 125, 112, 249, 83, 55, 187, 131, 206] h = list(wrong()) h = [h[i] ^ xor[i] for i in range(16)] b = 16 * [None] x = [] for i in range(16): x.append(BitVec('x' + str(i), 32)) b[0] = x[2] ^ x[3] ^ x[4] ^ x[8] ^ x[11] ^ x[14] b[1] = x[0] ^ x[1] ^ x[8] ^ x[11] ^ x[13] ^ x[14] b[2] = x[0] ^ x[1] ^ x[2] ^ x[4] ^ x[5] ^ x[8] ^ x[9] ^ x[10] ^ x[13] ^ x[14] ^ x[15] b[3] = x[5] ^ x[6] ^ x[8] ^ x[9] ^ x[10] ^ x[12] ^ x[15] b[4] = x[1] ^ x[6] ^ x[7] ^ x[8] ^ x[12] ^ x[13] ^ x[14] ^ x[15] b[5] = x[0] ^ x[4] ^ x[7] ^ x[8] ^ x[9] ^ x[10] ^ x[12] ^ x[13] ^ x[14] ^ x[15] b[6] = x[1] ^ x[3] ^ x[7] ^ x[9] ^ x[10] ^ x[11] ^ x[12] ^ x[13] ^ x[15] b[7] = x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[8] ^ x[10] ^ x[11] ^ x[14] b[8] = x[1] ^ x[2] ^ x[3] ^ x[5] ^ x[9] ^ x[10] ^ x[11] ^ x[12] b[9] = x[6] ^ x[7] ^ x[8] ^ x[10] ^ x[11] ^ x[12] ^ x[15] b[10] = x[0] ^ x[3] ^ x[4] ^ x[7] ^ x[8] ^ x[10] ^ x[11] ^ x[12] ^ x[13] ^ x[14] ^ x[15] b[11] = x[0] ^ x[2] ^ x[4] ^ x[6] ^ x[13] b[12] = x[0] ^ x[3] ^ x[6] ^ x[7] ^ x[10] ^ x[12] ^ x[15] b[13] = x[2] ^ x[3] ^ x[4] ^ x[5] ^ x[6] ^ x[7] ^ x[11] ^ x[12] ^ x[13] ^ x[14] b[14] = x[1] ^ x[2] ^ x[3] ^ x[5] ^ x[7] ^ x[11] ^ x[13] ^ x[14] ^ x[15] b[15] = x[1] ^ x[3] ^ x[5] ^ x[9] ^ x[10] ^ x[11] ^ x[13] ^ x[15] solver = Solver() for i in range(16): solver.add(x[i] < 128) for i in range(16): solver.add(b[i] == h[i]) if solver.check() == sat: m = solver.model() print(bytes([m[i].as_long() for i in x])) else: print('unsat')
, : 5C0G7TY2LWI2YXMB

0x08 — snake
The Flare team is attempting to pivot to full-time twitch streaming video games instead of reverse engineering computer software all day. We wrote our own classic NES game to stream content that nobody else has seen and watch those subscribers flow in. It turned out to be too hard for us to beat so we gave up. See if you can beat it and capture the internet points that we failed to collect.
NES
- . FCEUX
, .. . , .

, , 0x25
. , . NES
- IDA
. inesldr . 0x25
. C82A
, . 0x33
.

, — 0x32
0x25
. , . , FCEUX
. .

0x09 — reloadered
This is a simple challenge, enter the password, receive the key. I hear that it caused problems when trying to analyze it with ghidra. Remember that valid flare-on flags will always end with @flare-on.com
reloaderd.exe
, . , , . , , XOR
, @FLAG.com
, .

, NOP
. , , . . , . , , . , , NOP
, .
, , XOR
, . @flare-on.com
, . :
flag = bytearray(b'D)6\n)\x0f\x05\x1be&\x10\x04+h0/\x003/\x05\x1a\x1f\x0f8\x02\x18B\x023\x1a(\x04*G?\x04&dfM\x107>(>w\x1c?~64*\x00') for i in range(0x539): for j in range(0x34): if (i % 3) == 0 or (i % 7) == 0: flag[j] ^= (i & 0xff) end = b'@flare-on.com' def xor(a, b): return bytes([i^j for i, j in zip(a, b)]) for i in range(len(flag)): print(i, xor(end, flag[i:])) print(xor(flag, b'3HeadedMonkey'*4))

0x0A — Mugatu
Hello,
I'm working an incident response case for Derek Zoolander. He clicked a link and was infected with MugatuWare! As a result, his new headshot compilation GIF was encrypted.
To secure an upcoming runway show, Derek needs this GIF decrypted; however, he refuses to pay the ransom.
We received an additional encrypted GIF from an anonymous informant. The informant told us the GIF should help in our decryption efforts, but we were unable to figure it out.
We're reaching out to you, our best malware analyst, in hopes that you can reverse engineer this malware and decrypt Derek's GIF.
I've included a directory full of files containing:
- MugatuWare malware
- Ransom note (GIFtToDerek.txt)
- Encrypted headshot GIF (best.gif.Mugatu)
- Encrypted informant GIF (the_key_to_success_0000.gif.Mugatu)
Thanks,
Roy
:
- best.gif.Mugatu
- GIFtToDerek.txt
- Mugatuware.exe
- the_key_to_success_0000.gif.Mugatu
, , GIF
-. , .Mugatu
. Mugatuware.exe
. , — . , , .

IDA
, :
import ida_segment import ida_name import ida_bytes import ida_typeinf idata = ida_segment.get_segm_by_name('.idata') type_map = {} for addr in range(idata.start_ea, idata.end_ea, 4): name = ida_name.get_name(addr) if name: tp = ida_typeinf.idc_get_type(addr) if tp: type_map[name] = tp for addr in range(idata.start_ea, idata.end_ea, 4): imp = ida_bytes.get_dword(addr) if imp != 0: imp_name = ida_name.get_name(imp) name_part = imp_name.split('_')[-1] ida_name.set_name(addr, name_part + '_imp') if name_part in type_map: tp = type_map[name_part] ida_typeinf.apply_decl(addr, tp.replace('(', 'func(') + ';')
:

, , in-memory
PE
-. , CrazyPills!!!
. , . Sleep
, http
-. , , , . , , , . .

- :
- ;
- ;
Mailslots
;really, really, really, ridiculously good looking gifs
;.gif
. .Mugatu
. GIFtToDerek.txt
.
, — 8 . XOR
CrazyPills!!!
, . , :

XTEA
, — BYTE
, DWORD
. . Python
:
def crypt(a, b, key): i = 0 for _ in range(32): t = (i + key[i & 3]) & 0xffffffff a = (a + (t ^ (b + ((b >> 5) ^ (b << 4))))) & 0xffffffff i = (0x100000000 + i - 0x61C88647) & 0xffffffff t = (i + key[(i >> 11) & 3]) & 0xffffffff b = (b + (t ^ (a + ((a >> 5) ^ (a << 4))))) & 0xffffffff return a, b def decrypt(a, b, key): i = 0xc6ef3720 for _ in range(32): t = (i + key[(i >> 11) & 3]) & 0xffffffff b = (0x100000000 + b - (t ^ (a + ((a >> 5) ^ (a << 4))))) & 0xffffffff i = (i + 0x61C88647) & 0xffffffff t = (i + key[i & 3]) & 0xffffffff a = (0x100000000 + a - (t ^ (b + ((b >> 5) ^ (b << 4))))) & 0xffffffff return a, b
, the_key_to_success_0000.gif.Mugatu
. , . :

, , . C
. GIF
-.
#include <stdio.h> #include <unistd.h> void decrypt(unsigned int * inp, unsigned int * outp, unsigned char * key) { unsigned int i = 0xc6ef3720; unsigned int a = inp[0]; unsigned int b = inp[1]; unsigned int t; for(int j = 0; j < 32; j++) { t = i + key[(i >> 11) & 3]; b -= t ^ (a + ((a >> 5) ^ (a << 4))); i += 0x61C88647; t = i + key[i & 3]; a -= t ^ (b + ((b >> 5) ^ (b << 4))); } outp[0] = a; outp[1] = b; } int main() { int fd = open("best.gif.Mugatu", 0); unsigned int inp[2]; unsigned int outp[2]; unsigned int key = 0; read(fd, inp, 8); close(fd); for(unsigned long long key = 0; key < 0x100000000; key++) { if((key & 0xffffff) == 0) { printf("%lf\n", ((double)key) / ((double)0x100000000) * 100.0); } decrypt(inp, outp, &key); if( ((char *)outp)[0] == 'G' && ((char *)outp)[1] == 'I' && ((char *)outp)[2] == 'F' && ((char *)outp)[5] == 'a') { printf("%#llx\n", key); } } }
0xb1357331
:

0x0B — vv_max
Hey, at least its not subleq.
vv_max.exe
, . 256- . AVX2
, vpermd
, vpslld
. - :
0000 clear_regs 0001 r0 = 393130324552414c46 0023 r1 = 3030303030303030303030303030303030303030303030303030303030303030 0045 r3 = 1a1b1b1b1a13111111111111111111151a1b1b1b1a1311111111111111111115 0067 r4 = 1010101010101010080408040201101010101010101010100804080402011010 0089 r5 = b9b9bfbf041310000000000000000000b9b9bfbf04131000 00ab r6 = 2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f 00cd r10 = 140014001400140014001400140014001400140014001400140014001400140 00ef r11 = 1100000011000000110000001100000011000000110000001100000011000 0111 r12 = ffffffff0c0d0e08090a040506000102ffffffff0c0d0e08090a040506000102 0133 r13 = ffffffffffffffff000000060000000500000004000000020000000100000000 0155 r16 = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0177 r17 = 6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19 0199 r18 = 428a2f9871374491b5c0fbcfe9b5dba53956c25b59f111f1923f82a4ab1c5ed5 01bb r19 = 300000002000000010000000000000007000000060000000500000004 01dd r20 = 0 01ff r21 = 100000001000000010000000100000001000000010000000100000001 0221 r22 = 200000002000000020000000200000002000000020000000200000002 0243 r23 = 300000003000000030000000300000003000000030000000300000003 0265 r24 = 400000004000000040000000400000004000000040000000400000004 0287 r25 = 500000005000000050000000500000005000000050000000500000005 02a9 r26 = 600000006000000060000000600000006000000060000000600000006 02cb r27 = 700000007000000070000000700000007000000070000000700000007 02ed r20 = vpermd(r0, r20) 02f1 r21 = vpermd(r0, r21) 02f5 r22 = vpermd(r0, r22) 02f9 r23 = vpermd(r0, r23) 02fd r24 = vpermd(r0, r24) 0301 r25 = vpermd(r0, r25) 0305 r26 = vpermd(r0, r26) 0309 r27 = vpermd(r0, r27) 030d r7 = vpsrld(r1, 4) 0311 r28 = r20 ^ r21 0315 r28 = r28 ^ r22 0319 r28 = r28 ^ r23 031d r28 = r28 ^ r24 0321 r28 = r28 ^ r25 0325 r28 = r28 ^ r26 0329 r28 = r28 ^ r27 032d r7 = r7 & r6 0331 r29 = vpslld(r17, 7) 0335 r30 = vpsrld(r17, 25) 0339 r15 = r29 | r30 033d r8 = vpcmpeqb(r1, r6) 0341 r29 = vpslld(r17, 21) 0345 r30 = vpsrld(r17, 11) 0349 r29 = r29 | r30 034d r15 = r15 ^ r29 0351 r8 = vpcmpeqb(r1, r6) 0355 r29 = vpslld(r17, 26) 0359 r30 = vpsrld(r17, 6) 035d r29 = r29 | r30 0361 r15 = r15 ^ r29 0365 r29 = r20 ^ r16 0369 r30 = r20 & r18 036d r29 = r29 ^ r30 0371 r15 = add_d(r29, r15) 0375 r20 = add_d(r15, r0) 0379 r7 = add_b(r8, r7) 037d r29 = r20 ^ r28 0381 r17 = vpermd(r29, r19) 0385 r7 = vpshufb(r5, r7) 0389 r29 = vpslld(r17, 7) 038d r30 = vpsrld(r17, 25) 0391 r15 = r29 | r30 0395 r29 = vpslld(r17, 21) 0399 r30 = vpsrld(r17, 11) 039d r29 = r29 | r30 03a1 r15 = r15 ^ r29 03a5 r29 = vpslld(r17, 26) 03a9 r30 = vpsrld(r17, 6) 03ad r29 = r29 | r30 03b1 r15 = r15 ^ r29 03b5 r2 = add_b(r1, r7) 03b9 r29 = r21 ^ r16 03bd r30 = r21 & r18 03c1 r29 = r29 ^ r30 03c5 r15 = add_d(r29, r15) 03c9 r21 = add_d(r15, r0) 03cd r29 = r21 ^ r28 03d1 r17 = vpermd(r29, r19) 03d5 r20 = r20 ^ r21 03d9 r29 = vpslld(r17, 7) 03dd r30 = vpsrld(r17, 25) 03e1 r15 = r29 | r30 03e5 r29 = vpslld(r17, 21) 03e9 r30 = vpsrld(r17, 11) 03ed r29 = r29 | r30 03f1 r15 = r15 ^ r29 03f5 r29 = vpslld(r17, 26) 03f9 r30 = vpsrld(r17, 6) 03fd r29 = r29 | r30 0401 r15 = r15 ^ r29 0405 r7 = vpmaddubsw(r2, r10) 0409 r29 = r22 ^ r16 040d r30 = r22 & r18 0411 r29 = r29 ^ r30 0415 r15 = add_d(r29, r15) 0419 r22 = add_d(r15, r0) 041d r29 = r22 ^ r28 0421 r17 = vpermd(r29, r19) 0425 r20 = r20 ^ r22 0429 r29 = vpslld(r17, 7) 042d r30 = vpsrld(r17, 25) 0431 r15 = r29 | r30 0435 r29 = vpslld(r17, 21) 0439 r30 = vpsrld(r17, 11) 043d r29 = r29 | r30 0441 r15 = r15 ^ r29 0445 r29 = vpslld(r17, 26) 0449 r30 = vpsrld(r17, 6) 044d r29 = r29 | r30 0451 r15 = r15 ^ r29 0455 r2 = vpmaddwd(r7, r11) 0459 r29 = r23 ^ r16 045d r30 = r23 & r18 0461 r29 = r29 ^ r30 0465 r15 = add_d(r29, r15) 0469 r23 = add_d(r15, r0) 046d r29 = r23 ^ r28 0471 r17 = vpermd(r29, r19) 0475 r20 = r20 ^ r23 0479 r29 = vpslld(r17, 7) 047d r30 = vpsrld(r17, 25) 0481 r15 = r29 | r30 0485 r29 = vpslld(r17, 21) 0489 r30 = vpsrld(r17, 11) 048d r29 = r29 | r30 0491 r15 = r15 ^ r29 0495 r29 = vpslld(r17, 26) 0499 r30 = vpsrld(r17, 6) 049d r29 = r29 | r30 04a1 r15 = r15 ^ r29 04a5 r29 = r24 ^ r16 04a9 r30 = r24 & r18 04ad r29 = r29 ^ r30 04b1 r15 = add_d(r29, r15) 04b5 r24 = add_d(r15, r0) 04b9 r29 = r24 ^ r28 04bd r17 = vpermd(r29, r19) 04c1 r20 = r20 ^ r24 04c5 r29 = vpslld(r17, 7) 04c9 r30 = vpsrld(r17, 25) 04cd r15 = r29 | r30 04d1 r29 = vpslld(r17, 21) 04d5 r30 = vpsrld(r17, 11) 04d9 r29 = r29 | r30 04dd r15 = r15 ^ r29 04e1 r29 = vpslld(r17, 26) 04e5 r30 = vpsrld(r17, 6) 04e9 r29 = r29 | r30 04ed r15 = r15 ^ r29 04f1 r29 = r25 ^ r16 04f5 r30 = r25 & r18 04f9 r29 = r29 ^ r30 04fd r15 = add_d(r29, r15) 0501 r25 = add_d(r15, r0) 0505 r29 = r25 ^ r28 0509 r17 = vpermd(r29, r19) 050d r20 = r20 ^ r25 0511 r2 = vpshufb(r2, r12) 0515 r29 = vpslld(r17, 7) 0519 r30 = vpsrld(r17, 25) 051d r15 = r29 | r30 0521 r29 = vpslld(r17, 21) 0525 r30 = vpsrld(r17, 11) 0529 r29 = r29 | r30 052d r15 = r15 ^ r29 0531 r29 = vpslld(r17, 26) 0535 r30 = vpsrld(r17, 6) 0539 r29 = r29 | r30 053d r15 = r15 ^ r29 0541 r29 = r26 ^ r16 0545 r30 = r26 & r18 0549 r29 = r29 ^ r30 054d r15 = add_d(r29, r15) 0551 r26 = add_d(r15, r0) 0555 r29 = r26 ^ r28 0559 r17 = vpermd(r29, r19) 055d r20 = r20 ^ r26 0561 r29 = vpslld(r17, 7) 0565 r30 = vpsrld(r17, 25) 0569 r15 = r29 | r30 056d r29 = vpslld(r17, 21) 0571 r30 = vpsrld(r17, 11) 0575 r29 = r29 | r30 0579 r15 = r15 ^ r29 057d r29 = vpslld(r17, 26) 0581 r30 = vpsrld(r17, 6) 0585 r29 = r29 | r30 0589 r15 = r15 ^ r29 058d r2 = vpermd(r2, r13) 0591 r29 = r27 ^ r16 0595 r30 = r27 & r18 0599 r29 = r29 ^ r30 059d r15 = add_d(r29, r15) 05a1 r27 = add_d(r15, r0) 05a5 r29 = r27 ^ r28 05a9 r17 = vpermd(r29, r19) 05ad r20 = r20 ^ r27 05b1 r19 = ffffffffffffffffffffffffffffffffffffffffffffffff 05d3 r20 = r20 & r19 05d7 r31 = 2176620c3a5c0f290b583618734f07102e332623780e59150c05172d4b1b1e22
FLARE2019
. , , . , FLARE2019
. r2
r20
. , r20
. r2
— 6 r2
. , 6 . Frida
:
// vvmax.js var modules = Process.enumerateModules(); var base = modules[0].base; Interceptor.attach(base.add(0x1665), function() { var p = this.context.rdx.add(0x840); var res = p.readByteArray(32); send(null, res); });
:

0x0C — help
You're my only hope FLARE-On player! One of our developers was hacked and we're not sure what they took. We managed to set up a packet capture on the network once we found out but they were definitely already on the system. I think whatever they installed must be buggy — it looks like they crashed our developer box. We saved off the dump file but I can't make heads or tails of it — PLEASE HELP!!!!!!
. RAM
- . 4444
, 6666
, 7777
8888
. , , , RAM
-. volatility
. volatility
Win10x64_15063
, , Win7SP1x64
, .
volatility
:
$ volatility --profile Win7SP1x64 -f help.dmp modules Volatility Foundation Volatility Framework 2.6 Offset(V) Name Base Size File ------------------ -------------------- ------------------ ------------------ ---- 0xfffffa800183e890 ntoskrnl.exe 0xfffff80002a49000 0x5e7000 \SystemRoot\system32\ntoskrnl.exe ... 0xfffffa800428ff30 man.sys 0xfffff880033bc000 0xf000 \??\C:\Users\FLARE ON 2019\Desktop\man.sys
:
$ volatility --profile Win7SP1x64 -f help.dmp moddump --base 0xfffff880033bc000 -D drivers Volatility Foundation Volatility Framework 2.6 Module Base Module Name Result ------------------ -------------------- ------ 0xfffff880033bc000 man.sys Error: e_magic 0000 is not a valid DOS signature.
, . volshell
.
$ volatility --profile Win7SP1x64 -f help.dmp volshell In [1]: db(0xfffff880033bc000) 0xfffff880033bc000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0xfffff880033bc070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ In [2]: db(0xfffff880033bc000 + 0x1100) 0xfffff880033bd100 01 48 8b 4c 24 20 48 8b 44 24 28 48 89 41 08 48 .HL$.HD$(HAH 0xfffff880033bd110 83 c4 18 c3 cc cc cc cc cc cc cc cc cc cc cc cc ................ 0xfffff880033bd120 48 89 4c 24 08 48 83 ec 38 48 8b 44 24 40 0f be HL$.H..8H.D$@.. 0xfffff880033bd130 48 43 48 8b 44 24 40 0f be 40 42 83 c0 01 3b c8 HCH.D$@..@B...;. 0xfffff880033bd140 7e 27 45 33 c9 41 b8 15 5b 00 00 48 8d 15 de 44 ~'E3.A..[..H...D 0xfffff880033bd150 00 00 48 8d 0d 07 45 00 00 ff 15 71 4f 00 00 c7 ..H...E....qO... 0xfffff880033bd160 44 24 20 00 00 00 00 eb 08 c7 44 24 20 01 00 00 D$........D$.... 0xfffff880033bd170 00 48 8b 44 24 40 48 8b 80 b8 00 00 00 48 83 c4 .HD$@H......H.. In [4]: man = addrspace().read(0xfffff880033bc000, 0xf000) In [5]: with open('man_writeup.sys', 'wb') as f: ...: f.write(man) ...:
, , moddump
. . . - , , .
RC4
. , .
user-space
. DLL
- . DLL
- ( m.dll
), . , . :
- (
+0x8
) _EPROCESS
( +0x68
)- (
+0x48
) - (
+0x58
)
DLL
- RC4
, 0x2c
- , 0x48
.
volatility
volshell
:
import struct from Crypto.Cipher import ARC4 head = 0xfffff880033c8158 krnl = addrspace() def u64(x): return struct.unpack('Q', x)[0] fd = u64(krnl.read(head, 8)) while True: proc_addr = u64(krnl.read(fd + 0x68, 8)) base = u64(krnl.read(fd + 0x48, 8)) key = krnl.read(fd + 0x48, 0x2c) sz = u64(krnl.read(fd + 0x58, 8)) fd = u64(krnl.read(fd, 8)) p = obj.Object('_EPROCESS', proc_addr, krnl) print p.ImageFileName.v(), hex(proc_addr), hex(base), hex(sz) proc_space = p.get_process_address_space() dump = proc_space.read(base, sz) if dump[:0x100] == '\x00' * 0x100: dump = ARC4.new(key).decrypt(dump) with open('proc_{:016x}'.format(base), 'wb') as f: f.write(dump) if fd == head: break
, , RC4
. IDA
, , :
IDA from __future__ import print_function import sys import re from idaapi import get_func, decompile, get_name_ea, auto_wait, BADADDR from idaapi import cot_call, cot_obj, init_hexrays_plugin, qexit import ida_typeinf import ida_lines def rc4(key, data): S = list(range(256)) j = 0 for i in list(range(256)): j = (j + S[i] + ord(key[i % len(key)])) % 256 S[i], S[j] = S[j], S[i] j = 0 y = 0 out = [] for char in data: j = (j + 1) % 256 y = (y + S[j]) % 256 S[j], S[y] = S[y], S[j] out.append(chr(ord(char) ^ S[(S[j] + S[y]) % 256])) return ''.join(out) def decrypt_stack_str_args(ea): func = get_func(ea) if func is None: return try: c_func = decompile(func) c_func.pseudocode except Exception as ex: return for citem in c_func.treeitems: citem = citem.to_specific_type if citem.is_expr() and\ citem.op == cot_call and\ citem.ea == ea: args = [] key = citem.a[0] key_len = citem.a[1] s = citem.a[2] s_len = citem.a[3] def get_var_idx(obj): while obj.opname != 'var': if obj.opname in ('ref', 'cast'): obj = obj.x else: raise Exception('can\'t find type') return obj.v.idx if key_len.opname != 'num' or s_len.opname != 'num': print('[!] can\'t get length: 0x{:08x}'.format(ea)) else: try: key_len_val = key_len.n._value s_len_val = s_len.n._value print('0x{:08x}'.format(ea), 'key_len =', key_len_val, ', s_len =', s_len_val) hx_view = idaapi.open_pseudocode(ea, -1) key_var_stkoff = hx_view.cfunc.get_lvars()[get_var_idx(key)].location.stkoff() s_var_stkoff = hx_view.cfunc.get_lvars()[get_var_idx(s)].location.stkoff() key_var = [v for v in hx_view.cfunc.get_lvars() if v.location.stkoff() == key_var_stkoff][0] tif = ida_typeinf.tinfo_t() ida_typeinf.parse_decl(tif, None, 'unsigned __int8 [{}];'.format(key_len_val), 0) hx_view.set_lvar_type(key_var, tif) s_var = [v for v in hx_view.cfunc.get_lvars() if v.location.stkoff() == s_var_stkoff][0] tif = ida_typeinf.tinfo_t() ida_typeinf.parse_decl(tif, None, 'unsigned __int8 [{}];'.format(s_len_val + 1), 0) hx_view.set_lvar_type(s_var, tif) key_var = [v for v in hx_view.cfunc.get_lvars() if v.location.stkoff() == key_var_stkoff][0] s_var = [v for v in hx_view.cfunc.get_lvars() if v.location.stkoff() == s_var_stkoff][0] key_regex = re.compile('{}\[(.+)\] = (.+);'.format(key_var.name)) s_regex = re.compile('{}\[(.+)\] = (.+);'.format(s_var.name)) key = bytearray(key_len_val) s = bytearray(s_len_val + 1) src = '\n'.join([ida_lines.tag_remove(i.line) for i in hx_view.cfunc.pseudocode]) for i, j in s_regex.findall(src): s[int(i)] = (0x100 + int(j)) & 0xff for i, j in key_regex.findall(src): key[int(i)] = (0x100 + int(j)) & 0xff key = ''.join(chr(i) for i in key) s = ''.join(chr(i) for i in s) result = rc4(key, s[:-1])
:

. :
m.dll
— , . 4444
. — ;n.dll
— 192.168.1.243
;c.dll
— RC4
. ;k.dll
— (keylogger);s.dll
— ;f.dll
— .
, XOR
8. 4444
, .. . : , — . , - .
( 4444
) . , - . , . :
- keys.kdb
- C:\
- C:\keypass\keys.kdb
, f.dll
: keys.kdb
, .
6666
. LZNT1
RC4
XOR
. , XOR
- , .. . RC4
, RAM
-: FLARE ON 2019
. , GetUserNameA
, , - , RC4
. LZNT1
:
from ctypes import * nt = windll.ntdll for fname in ['input']: with open(fname, 'rb') as f: buf = f.read() dec_data = create_string_buffer(0x10000) final_size = c_ulong(0) status = nt.RtlDecompressBuffer( 0x102,
6666
. :
00000000: CC 69 94 FA 6A 37 18 29 CB 8D 87 EF 11 63 8E 73 .i..j7.).....cs 00000010: FE AB 43 3B B3 94 28 4B 4D 19 00 00 00 4F DB C7 ..C;..(KM....O.. 00000020: F3 1E E4 13 15 34 8F 51 A9 2B C2 D7 C1 96 78 F7 .....4.Q.+....x. 00000030: 91 98
, :
00000000: 19 00 00 00 4F DB C7 F3 1E E4 13 15 34 8F 51 A9 ....O.......4.Q. 00000010: 2B C2 D7 C1 96 78 F7 91 98 +....x...
4 — , 25. :
00000000: 12 B0 00 43 3A 5C 6B 65 79 70 61 04 73 73 01 70 ...C:\keypa.ss.p 00000010: 73 2E 6B 64 62 s.kdb
C:\keypass\keys.kdb
. , , . 6666
— KeePass
.
7777
BMP
. XOR
, , , .. . , , KeePass
.


8888
k.dll
— .
C:\Windows\system32\cmd.exe nslookup googlecom ping 1722173110 nslookup soeblogcom nslookup fiosquatumgatefiosrouterhome C:\Windows\system32\cmd.exe Start Start menu Start menu chrome www.flare-on.com - Google Chrome tis encrypting something twice better than once Is encrypting something twice better than once? - Google Search - Google Chrome Start Start menu Start menu keeKeePass <DYN_TITLE> th1sisth33nd111 KeePass keys.kdb - KeePass Is encrypting something twice better than once? - Google Search - Google Chrome Start Start menu Start menu KeePass <DYN_TITLE> th1sisth33nd111 Open Database - keys.kdb KeePass Start Start menu Start menu KeePass Start menu Start menu Start menu KeePass <DYN_TITLE> th1sisth33nd111
th1sisth33nd111
, . , . , keylogger . , , ping
. hashcat
KeePass
, . :
$ strings help.dmp | grep -i '3nd!' !s_iS_th3_3Nd!!!
Th
.

. , -.
0x0D —
, . , , . , volatility
, . ( UTC+3:00):