# Code security 101 @silur <aside class="notes"> emphasize it's not a complete course and may have errors </aside> --- ## Agenda - Peptalk :face_with_rolling_eyes: - Current Stats - General attack types and approaches - The SDLC - OWASP top10 deep-dive - Mitigation workshop - Secure architecture patterns - DecSecOps - Threat Modelling --- ## Web security - Low-hangig fruits, OWASP top 10 - Server-side is not the only asset - Vuln bots and the cloud - Composite attacks --- ## Secure web coding: - Kerckhoff's principle - Properly handling secrets - Sanitazation - CSRF tokens - Iframe Sandboxing - Cookies and JWT - Service isolation - Service authorization - Supply chain considerations --- ## Native security primer: - X86 and ARM - Android - Old but gold, refresh on buffer overflows - Canaries & ASLR - GOT - Android Zygote isolation --- ## Native secure coding: - Properly handling secrets - hardening your C - NASA's way of secure C - AndroidManifests - To obfuscate or not? - (Crypto) API misuse on mobile - ARM trustzone - OP-TEE --- ## Why bother at all? ![image](https://hackmd.io/_uploads/BJx-BmMSp.png) --- ## Supply chain is indeed risky ![image](https://hackmd.io/_uploads/BJViuMzB6.png) --- ## ... And you can't react in time anymore ![image](https://hackmd.io/_uploads/ryF4KNzSp.png) --- ![image](https://hackmd.io/_uploads/B1R-XzGBT.png) --- ## Your web services are the most likely target ![image](https://hackmd.io/_uploads/HkRQmGzra.png) --- ![image](https://hackmd.io/_uploads/r15HQGzBp.png) --- - The most common attack vector in organizations is improper access control - This is usually a logical, not a supply-chain bug - Root cause is sometimes not software, these access controls might ACTUALLY be missing --- ![image](https://hackmd.io/_uploads/S1V8cEGST.png) <small>source: OWASP WSTG</small> --- ## Attacker personas --- ## Vijender Kumar ![image](https://hackmd.io/_uploads/BkZ85zMSa.png) - Low attention span - Only uses rapid7 and shellstorm - Mass-scans the whole IP4 range for low-hanging fruit - Easily defeated by WAFs and basic measures --- ## Business competitors ![image](https://hackmd.io/_uploads/ryJqAGGH6.png =x500) - Regurarly keeps updated with your operations - Does not invest much on offense (mostly recon) - Expertise in your industry, including your supply chain :warning: - Likely even shared coworkers with you --- [tsc code theft case](https://www.theregister.com/2023/11/24/tata_210m_code_theft/) [nvidia code theft case](https://www.theverge.com/2023/11/23/23973673/valeo-nvidia-autonomous-driving-software-ip-theft-lawsuit) --- ## Lone wolf blackhats ![image](https://wallpapercave.com/wp/wp2345439.jpg =x500) - High attention span and focus on you - Invests time and money into an attack that has no economic value beyond fame on IRC chats - Ahead of the industry, usually only on technical terms - The easier type of APT --- ## Organized APT ![image](https://hackmd.io/_uploads/By4lWmMSa.png =x500) - High attention span and focus - Considerable budget and organization - Sometimes even state-sponsored - Highly advanced TTPs involving human factors and on-site ops --- ## Web security - 98% of attackers go against websites and webapps - They swarm with low-hanging fruit (thanks npm :slightly_smiling_face: ) --- ## Webapps are the best kind of software <small>... from a user and business perspecitve `\(סּںסּَ )/ۜ`</small> - Browsers are on every platform and kindof standardized - No installation - UX/UI flexibility (independent of device vendors) - Engineering team is (kindof) unified - Fast time to market :) --- ## Webapps are the worst kind of softare <small>... from a security perspective `(╯°□°)╯`</small> - JS was never meant for large-scale logic - New framework every 2nd week, engineers always in the dark - Processes large amounts of user-supplied (aka untrusted) data - Interfaces with protected parts of the network (paid API, user DB etc) - Supply chain is unnecessarly large and fragmented, lots of abandonware - Fast time to market :( --- ## Low-hanging fruit ### OWASP TOP10 - Broken Access Control - Cryptographic Failures - Injection - Insecure Design - Security Misconfiguration - Legacy software - ID and Auth failures - Software and data integrity failure - Logging and monitoring failure - SSR --- ## Example 1 - Broken access control part 1 - View another user's basket - How would you store which user's data we render on the DOM? - What means the user can tamper with that? <aside class="notes"> edit `bid` in sessionStorage </aside> --- ## Example 2 - Broken access control part 2 - We can't only mess up read access with BAC - Let's send a service feedback but from someone else's account! <aside class="notes"> intercept http://localhost:3000/api/Feedbacks </aside> --- ## Cryptographic erros - Never brew your own - There's _always_ a security geek in your team, consult them if in doubt - There's _always_ a better security geek oustide the team, consult with them too - Follow well-reputed standards from tech leaders and institutions such as NIST, OWASP, FIPS, Google --- ## Insecure hash ``` | d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f89 55ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5b d8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0 e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70 ``` ``` | d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f89 55ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5b d8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0 e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70 ``` Each of these blocks has MD5 hash `79054025255fb1a26e4bc422aef54eb4` --- ## Symmetric encryption Insecure (or reused) IV ```c EVP_CIPHER_CTX ctx; char key[EVP_MAX_KEY_LENGTH]; char iv[EVP_MAX_IV_LENGTH]; RAND_bytes(key, b); memset(iv,0,EVP_MAX_IV_LENGTH); // insecure getrandom(iv, EVP_MAX_IV_LENGTH, 0) // secure EVP_EncryptInit(&ctx,EVP_bf_cbc(), key,iv); ``` --- ```js // we use xsalsa20poly1305 msg1 = "This is a secret message!!!!!!!!!!!!!!!!!!!!!!!!!!!!" msg2 = "***************************More secret stuff here***" secret_key = [252, 225, 194, 245, 50, 74, 200, 62, 171, 0, 232, 145, 225, 127, 41, 81, 81, 251, 42, 168, 34, 184, 60, 137, 168, 122, 88, 68, 189, 219, 123, 112] nonce = [147, 3, 123, 195, 76, 217, 196, 102, 214, 10, 144, 88, 23, 135, 163, 60, 46, 86, 20, 249, 39, 70, 110, 205] ciphertext1 = encrypt(msg1, nonce, secret_key) ciphertext2 = encrypt(msg2, nonce, secret_key) ``` --- ```js xor_cipher = [] for i in range(len(ciphertext1)): xor_cipher.append(operator.xor(ciphertext1[i], ciphertext2[i])) for d in range(32,127): xor_temp = [] for i in range(len(xor_cipher)): xor_temp.append(operator.xor(xor_cipher[i], d)) xor_str = '' for i in range(len(xor_temp)): if xor_temp[i] == 0: xor_str += ' ' else: xor_str += chr(xor_temp[i]) print(xor_str) ``` --- ![image](https://github.com/christianlundkvist/blog/raw/master/2021_01_25_nonce_reuse_in_encryption/files/program_printout.jpeg?raw=true) --- ## Asymmetric encryption Never sign messages just blindly ```python p, q , e = rsa_gen_key() n, d = rsa_pubkey(p, q, e) ``` --- Let's imagine your hash is weak as in the following example, or that your implementation accepts unhashed inputs: ```python intercepted_ciphertext = 0xdeadbeef def sign(msg) return (msg^e) % n // insecure! def attack(): decrypt(intercepted_ciphertext + 0xcafebabe) // c2 = (intercepted_ciphertext + 0xcafebabe) ^ n // c2 = intercepted_ciphertext^e + 0xcafebabe^e // factor out 0xcafebabe and you have the original plaintext ``` --- NAAAH but who ever uses RSA without hashes and padding? If your padding system leaks validity checks, it basically becomes a tool to decrypt any ciphertext: https://github.com/flast101/padding-oracle-attack-explained --- - Nesting Asymmetric and Symmetric crypto is okay - but not if you place the keys next to the ciphertext --- ## Injection The vulnerability that dominated the internet untill 2016.... and still dominates all of PHP :shrug: --- :angel: ```js async function loginUser(req, res) { const hashedPasswordQuery = `SELECT password FROM users WHERE email = '${req.email}'`; // insecure } ``` :smiling_imp: ``` curl 'http://example.com/loginUser' --data 'email=\' OR 1=1; --' ``` query becomes `SELECT password FROM users WHERE email = '' OR 1=1; --'`; --- :angel: ```html <html> <body> <?php print "Not found: " . urldecode($_SERVER["REQUEST_URI"]); ?> </body> </html> ``` :smiling_imp: ``` curl 'http://example.com/<script>alert("pwned");</script>' ``` What happens if you try to print the path? You get JS executed :) --- ## Insecure design - A cinema chain allows group booking discounts and has a maximum of fifteen attendees before requiring a deposit. - A retail chain’s e-commerce website does not have protection against bots run by scalpers buying high-end video cards to resell auction websites. --- ## Security misconfiguration :angel: ```xml= <Directory /> Require all granted </Directory> ``` :smiling_imp: ```bash= curl 'http://example.com/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd' ``` --- ```java public BankAccount getUserBankAccount(String username, String accountNumber) { BankAccount userAccount = null; String query = null; try { if (isAuthorizedUser(username)) { query = "SELECT * FROM accounts WHERE owner = " + username + " AND accountID = " + accountNumber; DatabaseManager dbManager = new DatabaseManager(); Connection conn = dbManager.getConnection(); Statement stmt = conn.createStatement(); ResultSet queryResult = stmt.executeQuery(query); userAccount = (BankAccount)queryResult.getObject(accountNumber); } } catch (SQLException ex) { String logMessage = "Unable to retrieve account information from database,\nquery: " + query; Logger.getLogger(BankManager.class.getName()).log(Level.SEVERE, logMessage, ex); } return userAccount; } ``` --- ## Supply chain issues ![image](https://hackmd.io/_uploads/S1ubatmBT.png) ![image](https://hackmd.io/_uploads/BJkk6KQBT.png) --- ## Identification and Authentication Failures :smiling_imp: ```bash ffuf -u 'http://example.com/login' \ --data "email=alice@example.com&pass=FUZZ" \ -w /usr/share/seclists/Passwords/2020-200_most_used_passwords.txt ``` --- ## Software and Data Integrity Failures ![image](https://hackmd.io/_uploads/rk6rf9XBp.png =x500) --- ![image](https://hackmd.io/_uploads/H1758zMBa.png) --- ## Logging and monitoring failure ![image](https://hackmd.io/_uploads/BJSRM9XBa.png) --- ## SSRF :angel: ```ascii= POST /meteorology/forecasts HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 113 weatherApi=http://data.weatherapp.com:8080/meterology/forecasts/check%3FcurrentDateTime%3D6%26cityId%3D1 ``` :smiling_imp: ``` POST /meteorology/forecasts HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 33 weatherApi=http://localhost/admin ``` --- ## Composite attacks - It's not enough to only protect each case separately - Maybe an internal user can make an SSRF with a magic cookie from tech support - The external (blackbox) pentest might miss this as it needs knowledge about this credential - .... But the cookie might leak during a phising attack <aside class="notes"> composite attacks usually start with highly sophisticated phising </aside> --- ## But my business just started, they don't even know my shop is there! - All cloud providers are mass-scanned 24/7 - It takes approx 16 SECONDS from renting a VPS to having an automated vuln scanner bombing it - By the time you even start configuring apache, you might get locked out by the VPS due to spam --- # Web defenses __"one ought to design systems under the assumption that the enemy will immediately gain full familiarity with them".__ ~ Kerckhoffs --- - Moving your SSH port from port 22 to 922 is <u>Obscurity</u>. This is like hiding an open safe under Metro line 3. - Disabling password auth and X11 forwarding is <u>Security</u>. It takes more than just network knowledge and maths to factor ECDSA. --- ## Golden trick 1 Never echo back or even process RAW data from users. Every user with every user interaction is malicious. Period. <small>for real it'll make your job way easier :)</small> --- ## Access control For startups who don't have time for this: - Supabase - Oauth --- For architects who don't trust thirdparties with security (as you should): - Never let the user alter the control flow of auth. She is the very subject of it! --- For serious security geeks: - Google zanzibar - Ory.sh --- But overall: - Identities and credentials for authorized users, services, and hardware are managed by the organization - Identities are proofed and bound to credentials based on the context of interactions - Users, services, and hardware are authenticated - Physical access to assets is managed, monitored, and enforced commensurate with risk - Every auth/access event is attributable, accountable and asserted to credentials! --- ## Cryptographic failures - Only use highly reputed frameworks - Don't ever try to be smart with them - Check up on your libs CVEs and reports regularly - Always consult and audit with a specialized entity when in doubt, a GDPR violation costs much more. --- ## Recommendation If you are unsure and want the basics: https://github.com/openpgpjs/openpgpjs - IETF standardized format - Easy to use - Used by protonmail - Independently verified by a reputable group :warning: --- If you wanna dirt your hands with lower-level stuff: - https://www.w3.org/TR/WebCryptoAPI/ - W3C standardized - Easy to misuse :warning: - More performant and builtin in most browsers --- ## Injection - Sanitize, Sanitize, Sanitize! - Better if you reduce user-controlled input overall! --- ## SQL Your framework is guaranteed to have a high-level parametrized call that already sanitizes every param: ```nodejs= const text = 'INSERT INTO users(name, email) VALUES($1, $2) RETURNING *' const values = ['brianc', 'brian.m.carlson@gmail.com'] const res = await client.query(text, values) ``` If you can't find one, make an SQL prepared statement manually. **Never try to come up with an escape rule on your own. It's an NP-complete problem** --- ## XSS use https://github.com/cure53/DOMPurify ```js import * as DOMPurify from 'dompurify'; const clean = DOMPurify.sanitize('<b>hello there</b>'); ``` --- ## Insecure design Follow https://arc42.org/ Re-evaluate the stakeholders and the business requirements again... When it comes to money, everyone becomes a security expert suddenly! Especially when communicating the design is easier! --- ## Security misconfiguration Do automated security in your DevOps pipeline! ![image](https://hackmd.io/_uploads/BJlbDLGSp.png) --- Use [Semgrep](https://semgrep.dev) ```bash= python3 -m pip install semgrep cd myapp python -m semgrep . ``` --- Use the free [ZAProxy full scan CI job](https://github.com/marketplace/actions/zap-full-scan) ... or one of the paid actions from security companies ... or heck write your own shell scripts based on this talk! --- Use [Bunkerweb](https://www.bunkerity.com/en) - Nginx Proxy - Owasp core rule set - Rate limiting - Auto SSL - Automatically bans Tor and VPNs - Bruteforce protection - Injection protection - Dynamic config - Much more! --- ## Identificaion and Auth Auth0 for the lazy (and the managers) ```bash npm install @auth0/auth0-react ``` ```javascript import { useAuth0 } from "@auth0/auth0-react"; import React from "react"; const LoginButton = () => { const { loginWithRedirect } = useAuth0(); return <button onClick={() => loginWithRedirect()}> Log In </button>; }; ``` --- ## Supabase for the GDPR concious ```javascript import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') async function signUpNewUser() { const { data, error } = await supabase.auth.signUp({ email: 'example@email.com', password: 'example-password', options: { emailRedirectTo: 'https//example.com/welcome' } }) } async function signInWithEmail() { const { data, error } = await supabase.auth.signInWithPassword({ email: 'example@email.com', password: 'example-password' }) } ``` --- ## Recommendations for the manual laborer - JWTs are OK as long as you encrypt them properly. - Encrypt your cookies too - Enforce SameSite (done by default on bunkerweb) - Implement Passkeys (WebAuthN) ASAP - If you REALLY need to still do legacy passwords, use Argon2 - Keep sessions short in redis at serverside --- ## Ory.sh for the chads - Bunch of FOSS, state-of-the art products for Auth, SAML etc - Some (google zanzibar) designed by tech titans such as google - Hard to grasp and integrate but scales very well and reusable across all your products --- ## Software integrity - Your production software (and preferably all data provided by your org) has to be signed - I'd personally ECDSA sign even official tweets with threshold keys from PR --- ## For windows ```bash New-SelfSignedCertificate -Type Custom -Subject "CN=Contoso Software, O=Contoso Corporation, C=US" -KeyUsage DigitalSignature -FriendlyName "Your friendly name goes here" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}") SignTool sign /fd sha256 /n mycert myapp.msix ``` --- ## For java/android ```bash! keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 4096 -validity 365 jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA256 -keystore my-release-key.keystore my_application.apk alias_name # optional for android zipalign -v 4 my_application.apk my_application-aligned.apk ``` --- ## Logging and monitoring - Use a [SIEM] - Use an EDR/XDR - Ship your logs to the SIEM don't just pay for it :upside_down_face: --- ## Recommendation [Wazuh](https://wazuh.com/) ![image](https://hackmd.io/_uploads/H1eQPimB6.png =x400) - FOSS & Self-hosted - Extremely performant - Used by Cisco, Ebay, NASA, Salesforce --- ## SSRF - Your infra should never face WAN - Only the kuber cluster, docker swarm or VPN - Quick'n'dirty way - an SSH tunnel --- ```bash! # ssh -L localPort:remoteHost:remotePort user@remote ssh -L 1979:localhost:3000 webapp@example.com # manage your web admin without listening on WAN ``` --- # Misc tip from previous top10 - Use `iframe` with `sandbox` option - If your framework doesn't do CSRF tokens ~~drop it~~ generate your own from `/dev/urandom` on serverside - Those HTTP headers MATTER --- ![image](https://hackmd.io/_uploads/BykBAnXra.png =x500) --- # Native apps ## Desktop, X86 and ARM --- ![image](https://i.stack.imgur.com/4I3jv.png) --- ## Windows address space ![image](https://hackmd.io/_uploads/B1khd2XHa.png =x500) --- ![image](https://textbook.cs161.org/assets/images/memory-safety/x86/stack10.png) --- ![image](https://hackmd.io/_uploads/BJC7K3XSp.png) ```c! void myFunc(int a, int b, int c, int d, int e, int f); // MOV RCX, a // MOV RDX, b // MOV R8, c // MOV R9, d // PUSH f // PUSH e ``` --- ## Old but gold ```c char name[4]; // write past end of buffer (buffer overflow) strcpy(name,"a string longer than 4 characters"); // read past end of buffer (also not a good idea) printf("%s\n",name[6]); ``` --- ```c! #include <string.h> #include <stdio.h> void foo(char *bar) { float My_Float = 10.5; // Addr = 0x0023FF4C char c[28]; // Addr = 0x0023FF30 // Will print 10.500000 printf("My Float value = %f\n", My_Float); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Memory map: @ : c allocated memory # : My_Float allocated memory *c *My_Float 0x0023FF30 0x0023FF4C | | @@@@@@@@@@@@@@@@@@@@@@@@@@@@##### foo("my string is too long !!!!! XXXXX"); memcpy will put 0x1010C042 (little endian) in My_Float value. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ memcpy(c, bar, strlen(bar)); // no bounds checking... // Will print 96.031372 printf("My Float value = %f\n", My_Float); } int main(int argc, char **argv) { foo("my string is too long !!!!! \x10\x10\xc0\x42"); return 0; } ``` --- Let's pwn an actual overflow ```bash! wget https://github.com/mattetti/IOLI-crackme/raw/master/bin-linux/crackme0x00 r2 crackme0x00 > aaaa ``` --- ``` 80486e3: e8 38 fd ff ff call 8048420 <strcmp@plt> 80486e8: 83 c4 10 add esp,0x10 80486eb: 85 c0 test eax,eax 80486ed: 75 3a jne 8048729 <start+0x8c> 80486ef: 83 ec 0c sub esp,0xc -> 80486f2: 68 5e 88 04 08 push 0x804885e 80486f7: e8 74 fd ff ff call 8048470 <puts@plt> ... 804872c: 68 92 88 04 08 push 0x8048892 8048731: e8 3a fd ff ff call 8048470 <puts@plt> 8048736: 83 c4 10 add esp,0x10 ``` --- ```bash! $ echo AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJ > input $ ./crackme0x00 < input $ dmesg | tail -1 [238584.915883] crackme0x00[1095]: segfault at 48484848 ip 0000000048484848 sp 00000000ffffd6a0 error 14 ``` --- ```python from pwn import * p = process("./crackme0x00") p.sendline(cyclic(50)) #aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama p.interactive() # segfault at 61616168 ip 0000000061616168 cyclic_find(0x61616168) # 28 ``` --- ```python! from pwn import * shellcode = shellcraft.sh() from pwn import * p = process("./crackme0x00") shellcode = shellcraft.sh() p.sendline("A"*28+shellcode) p.interactive() # \o/ ``` --- # Realitiy punches in the face DATA regions are not executable for a while... ...But you can always reuse code from .TEXT :smiling_imp: --- ## Stack canaries They don't PREVENT overflows but detect and can enforce early abort. ![image](https://hackmd.io/_uploads/S1EPdTmSp.png =x300) <small>Usually a random value starting with `0x00` to halt all string functions</small> --- ## ASLR - randomized address space to prevent reliable jumping --- # How to defend (in C) Long version: ```bash gcc -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -ftrivial-auto-var-init=pattern -fPIE -pie -Wl,-z,relro,-z,now -fstack-protector-strong -fstack-clash-protection -fcf-protection=full myprogram.c ``` Short version (after GCC 14.0): ```bash gcc -fhardened myprogram.c ``` <small>... or just use a memory-safe lingo `¯\_(ツ)_/¯`</small> --- ## NASA's 10 rules fore safety-critical code - Restrict all code to very simple control flow constructs—do not use goto statements, setjmp or longjmp constructs, or direct or indirect recursion. - Give all loops a fixed upper bound. - Do not use dynamic memory allocation after initialization. - No function should be longer than what can be printed on a single sheet of paper --- - The code's assertion density should average to minimally two assertions per function. - Declare all data objects at the smallest possible level of scope. - Each calling function must check the return value of nonvoid functions, and each called function must check the validity of all parameters provided by the caller. --- - The use of the preprocessor must be limited to the inclusion of header files and simple macro definitions. - Limit pointer use to a single dereference, and do not use function pointers. - Compile with all possible warnings active; all warnings should then be addressed before the release of the software. --- ## Anti-reverse & Anti-debug ### PEB (process environment block) - BeingDebugged / isDebuggerPresent() - NtGlobalFlag - StartupInfo --- - set THREADINFOCLASS flag to ThreadHideFromDebugger to start hidden threads - alternatively use NtCreateThreadEx with `THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER` - NtSetInformationProcess - detect tracing - NtQuerySystemInformation - bunch of debug symbols here - NtUserBlockInput - yes this works --- ## Virtual machines? - some advanced DRM solutions build their own proprietary bytecode VM - ... which also proves ineffective against teenagers - And sometimes symbolic executors --- ## Fuzzers - A type of automatic tester that injects "structure junk" - Can work with every type of input, CLI, files even networks - In most cases it needs instrumentation but can work in blackbox - Not just for C/C++ - https://github.com/google/AFL --- ## Esil, Capstone, Angr ```python! p = angr.Project('sender', auto_load_libs=False) # Start with a blank state at the EIP after "key.txt" is read state = p.factory.blank_state(addr=0x401198) # Initialize global variables ADDR_PW_ORI = state.regs.ebp - 0x80004 ADDR_PW_ENC = ADDR_PW_ORI + 0x10000 ADDR_HASH = state.regs.ebp - 0x40000 # Setup stack to simulate the state after which the "key.txt" is read state.regs.esi = LEN_PW for i in range(LEN_PW): state.mem[ADDR_PW_ORI+i:].byte = state.solver.BVS('pw', 8) # Hook instructions to use a separate buffer for the XOR-ing function p.hook(0x401259, hook_duplicate_pw_buf, length=0) p.hook(0x4011E7, hook_use_dup_pw_buf, length=0) # To avoid calling imports (HeapAlloc), retrofit part of the stack as # temporary buffer to hold symbolic copy of the password p.hook(0x4011D6, hook_heapalloc, length=5) # Explore the states until after the hash is computed sm = p.factory.simulation_manager(state) sm.explore(find=0x4011EC) ``` --- # Mobile --- Android code runs on top of the ART VM, which is forked after boot by the Zygote process using a socket comm ART (and it's predecessor Dalvik) may be or may not (Oracle and Google is still sueing each other) a JVM --- On top of ART, each app has it's own isolated, sandboxed environment which can be even tightened with SELinux. Also the VM follows a permission-first system, Android was the first widely-adopted system with this idea --- ![image](https://hackmd.io/_uploads/HktOsCmHa.png) --- - Apps are packaged into `.apk` files which are basically ZIPs - They contain a `MANIFEST` which is XML with a bunch of descriptors - and a DEX file that has the actual bytecode --- ```bash wget 'https://dl.androidcontents.com/com.king.candycrushsaga/download?ecp=edb6fa9a664d82bfabe7089b706cd2e9&fn=com_king_candycrushsaga_v1.266.0.4.apk' -O candycrush.apk #jadx-gui candycrush.apk # or apktool d candycrush.apk --- ![image](https://hackmd.io/_uploads/B1u4RamB6.png =x500) --- - Java by nature is almost trivially reversible - You can protect with Proguard somewhat... - ... but there is ALWAYS a russian teenager with more time than you - Remember Kerckhoffs. You protect your app with cryptography, authorization and logic isolation NOT OBFUSCATION --- # Use the KeyChain API ```kotlin //Generate a key and store it in the KeyStore val keyGenerator: KeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); val keyGenParameterSpec: KeyGenParameterSpec = new KeyGenParameterSpec.Builder("MyKeyAlias", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) setUserAuthenticationValidityDurationSeconds(-1) // require fingerprint ever time .setRandomizedEncryptionRequired(true) //different ciphertext for same plaintext on each call .build(); keyGenerator.init(keyGenParameterSpec); keyGenerator.generateKey(); val key: SecretKey = keygen.generateKey(); ``` --- <small>did you find the vulnerability in the above code? :)</small> - The keystore API uses ARM Trustzone under the hood - It's a CC EAL5+ Secure element on most flagship phones - Almost the same security as a smartcard - The keys inside are EXTREMELY hard to read as they never actually reach the main CPU memory - Use this to encrypt your SharedPreferences and sensitive DB data --- ## What happens under the hood ```c! #define TA_HELLO_WORLD_CMD_INC_VALUE 0 TEEC_Result res; TEEC_Context ctx; TEEC_Session sess; TEEC_Operation op; TEEC_UUID uuid = TA_HELLO_WORLD_UUID; uint32_t err_origin; res = TEEC_InitializeContext(NULL, &ctx); if (res != TEEC_SUCCESS) errx(1, "TEEC_InitializeContext failed with code 0x%x", res); res = TEEC_OpenSession(&ctx, &sess, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin); if (res != TEEC_SUCCESS) errx(1, "TEEC_Opensession failed with code 0x%x origin 0x%x", res, err_origin); /* * Execute a function in the TA by invoking it, in this case * we're incrementing a number. * * The value of command ID part and how the parameters are * interpreted is part of the interface provided by the TA. */ /* Clear the TEEC_Operation struct */ memset(&op, 0, sizeof(op)); /* * Prepare the argument. Pass a value in the first parameter, * the remaining three parameters are unused. */ op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); op.params[0].value.a = 42; /* * TA_HELLO_WORLD_CMD_INC_VALUE is the actual function in the TA to be * called. */ printf("Invoking TA to increment %d\n", op.params[0].value.a); res = TEEC_InvokeCommand(&sess, TA_HELLO_WORLD_CMD_INC_VALUE, &op, &err_origin); if (res != TEEC_SUCCESS) errx(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x", res, err_origin); printf("TA incremented value to %d\n", op.params[0].value.a); TEEC_CloseSession(&sess); TEEC_FinalizeContext(&ctx); return 0; ``` --- ```kotlin val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") cipher.init(Cipher.ENCRYPT_MODE, key) val ciphertext: ByteArray = cipher.doFinal(plaintext) val iv: ByteArray = cipher.iv var sharedPref = getSharedPreferences("key", Context.MODE_WORLD_READABLE) var editor = sharedPref.edit() editor.putString("username", "administrator") editor.putString("password", iv + ciphertext) editor.commit() ``` --- More on ARM Trustzone and OP-TEE at https://optee.readthedocs.io/en/latest/index.html It's amazing stuff try it! --- ## Use [SQLCipher](https://www.zetetic.net/sqlcipher/sqlcipher-for-android/) to encrypt your whole app DB ```gradle implementation 'net.zetetic:sqlcipher-android:4.5.5@aar' implementation 'androidx.sqlite:sqlite:2.2.0' ``` ```kotlin System.loadLibrary("sqlcipher") val databaseFile = getDatabasePath("test.db") databaseFile.mkdirs() val database = SQLiteDatabase.openOrCreateDatabase(databaseFile, password, null, null, null) if (database != null) { database.execSQL("create table t1(a,b);") database.execSQL( "insert into t1(a,b) values(?, ?);", arrayOf("one for the money", "two for the show") ) } } ``` --- # Cert pinning put this into `res/xml/network_security_config.xml` ``` <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">example.com</domain> <pin-set expiration="2024-01-01"> <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> <!-- backup pin --> <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin> </pin-set> </domain-config> </network-security-config> ``` ... even when you ARE signed by a trusted CA --- ## Rely on https://developer.android.com/jetpack/androidx/releases/biometric for critical operations --- ## Anti-reverse? (meh) - Proguard - ApplicationInfo.FLAG_DEBUGGABLE - Debug.isDebuggerConnected() - Timer checks - /proc/pid/status --- ## Detect root (meh) - Yes, sandboxing is worthless on a rooted device - You can detect a rooted device with [rootbeer](https://github.com/scottyab/rootbeer) - But sensitive data should never be plaintext, even in isolated apps - Encrypt with the keystore and use X.509 - Authenticate both client and Server - Only use native android cryptography --- ## Wrapping up - 30% of security happens at code level - You don't have to be specifically targeted - DevSecOps is important, bunch of free tools for it - Half of your attacker personas fall out with a well-configured WAF - Don't obfuscate, encrypt and authorize! - TEE is okay if treated correctly - Get into passkeys asap! --- Spare a minute for a password policy questionary? ![image](https://hackmd.io/_uploads/S1_rKR4H6.png) https://tinyurl.com/2th7wtjd --- ## Thanks for your attention! Questions? https://silur.dev https://www.linkedin.com/in/silur/ @silur@infosec.exchange
{"title":"Code security 101","description":"type: slide","contributors":"[{\"id\":\"f4d4af67-750e-4c99-b33e-c04b6d99a6c6\",\"add\":39378,\"del\":5931}]"}
    243 views