I am a cyber security researcher and programmer.
Do you want to be one too? Check out my advice for learning hacking and programming.
You can support my work with a few bucks, here.
HTTP parameter discovery suite.
License: GNU Affero General Public License v3.0
I am a cyber security researcher and programmer.
Do you want to be one too? Check out my advice for learning hacking and programming.
You can support my work with a few bucks, here.
Hi, first of all, thanks for the amazing tool!
I usually use the tool using both --post
and --get
arguments, it would be cool to know which is which in the output. Because it prints all the parameters in the output but I don't get information about which is get params and which is post params. (Not sure if --post
and --get
work together this way, but if not, here is another idea! xD)
Thanks!
Hey Somdev brother, I am getting following error
python3 arjun.py -u https://www.caviar.com/ --get
_
/_| _ '
( |/ /(//) v1.3
_/
[] Analysing the content of the webpage] Now lets see how target deals with a non-existent parameter
[
[!] Reflections: 4
[!] Response Code: 200
[!] Content Length: 49313
[!] Plain-text Length: 18667
[] Parsing webpage for potential parameters] Performing heuristic level checks
[+] Heuristic found a potential parameter: username
[+] Prioritizing it
[+] Heuristic found a potential parameter: password
[+] Prioritizing it
[+] Heuristic found a potential parameter: _wpnonce
[+] Prioritizing it
[+] Heuristic found a potential parameter: _wp_http_referer
[+] Prioritizing it
[+] Heuristic found a potential parameter: login
[+] Prioritizing it
[+] Heuristic found a potential parameter: rememberme
[+] Prioritizing it
[+] Heuristic found a potential parameter: email
[+] Prioritizing it
[+] Heuristic found a potential parameter: email_2
[+] Prioritizing it
[+] Heuristic found a potential parameter: mailchimp_woocommerce_newsletter
[+] Prioritizing it
[+] Heuristic found a potential parameter: register
[+] Prioritizing it
[+] Heuristic found a potential parameter: EMAIL
[+] Prioritizing it
[+] Heuristic found a potential parameter: b_eccdcccded66e9f05a10d41a2_14f0b2626c
[+] Prioritizing it
[+] Heuristic found a potential parameter: s
[+] Prioritizing it
[+] Heuristic found a potential parameter: post_type
[+] Prioritizing it
[
Traceback (most recent call last):
File "arjun.py", line 183, in
toBeChecked = narrower(toBeChecked)
File "arjun.py", line 174, in narrower
if result.result():
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
return self.__get_result()
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "arjun.py", line 136, in quickBruter
newResponse = requester(url, joiner(params, include), headers, GET, delay)
File "/opt/tools/Arjun/core/requester.py", line 17, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/local/lib/python3.6/dist-packages/requests/api.py", line 72, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/api.py", line 58, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 512, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 644, in send
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 644, in
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 222, in resolve_redirects
**adapter_kwargs
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 622, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/adapters.py", line 410, in send
conn = self.get_connection(request.url, proxies)
File "/usr/local/lib/python3.6/dist-packages/requests/adapters.py", line 314, in get_connection
conn = self.poolmanager.connection_from_url(url)
File "/usr/local/lib/python3.6/dist-packages/urllib3/poolmanager.py", line 279, in connection_from_url
pool_kwargs=pool_kwargs)
File "/usr/local/lib/python3.6/dist-packages/urllib3/poolmanager.py", line 218, in connection_from_host
raise LocationValueError("No host specified.")
urllib3.exceptions.LocationValueError: No host specified.
The quit() function on the following line stops the program:
Lines 72 to 73 in a4d095c
Also when you're using a list of urls using the --urls parameter and a file with urls. This causes the progress to be lost.
Hi s0md3v,
The method
variable on https://github.com/s0md3v/Arjun/blob/master/arjun.py#L103 to get all the method in the form tag, But it wasn't used anywhere in the code later.
--
Regards,
bugbaba
Hi!
When a site uses HTTP Basic Authentication, Arjun is unable to detect parameters, since it results in Bad Request Errors.
The line causing it is headers['Host'] = re.search(r'https?://([^/]+)', url).group(1)
in requester.py, since commenting it out fixes the issue.
To replicate: Run Arjun on something like this: http://user:[email protected].
i'm trying to run in on windows i got the error
C:\Users\home\Desktop\Bugbounty\MybugBounty-Tool\python3
> python.exe ..\Arjun\arjun.py --url https://bugbountysite.ma/ --get
_
/_| _ '
( |/ /(//) v1.6
_/
The specified file for parameters doesn't exist
any help ?
Would be useful if delay could be set to float. Sometimes 1 second is too long and 0 too short.
Traceback (most recent call last):
File "arjun.py", line 115, in
url = stabilize(url)
File "arjun.py", line 97, in stabilize
if 'http' not in url:
TypeError: argument of type 'NoneType' is not iterable
root@sec:~/Arjun# python3 arjun.py -u http://testphp.vulnweb.com/cart.php --get -t 22 -o result.json
_
/_| _ '
( |/ /(//) v1.6
_/
[] Analysing the content of the webpage] Analysing behaviour for a non-existent parameter
[
[!] Reflections: 0
[!] Response Code: 200
[!] Content Length: 4903
[!] Plain-text Length: 1240
[] Parsing webpage for potential parameters] Performing heuristic level checks
[+] Heuristic found a potential post parameter: searchFor
[!] Prioritizing it
[+] Heuristic found a potential post parameter: goButton
[!] Prioritizing it
[
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 601, in urlopen
chunked=chunked)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 387, in _make_request
six.raise_from(e, None)
File "", line 3, in raise_from
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 383, in _make_request
httplib_response = conn.getresponse()
File "/usr/lib/python3.6/http/client.py", line 1356, in getresponse
response.begin()
File "/usr/lib/python3.6/http/client.py", line 307, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.6/http/client.py", line 289, in _read_status
raise BadStatusLine(line)
http.client.BadStatusLine:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/requests/adapters.py", line 440, in send
timeout=timeout
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 639, in urlopen
_stacktrace=sys.exc_info()[2])
File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 367, in increment
raise six.reraise(type(error), error, _stacktrace)
File "/usr/local/lib/python3.6/dist-packages/six.py", line 702, in reraise
raise value.with_traceback(tb)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 601, in urlopen
chunked=chunked)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 387, in _make_request
six.raise_from(e, None)
File "", line 3, in raise_from
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 383, in _make_request
httplib_response = conn.getresponse()
File "/usr/lib/python3.6/http/client.py", line 1356, in getresponse
response.begin()
File "/usr/lib/python3.6/http/client.py", line 307, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.6/http/client.py", line 289, in _read_status
raise BadStatusLine(line)
urllib3.exceptions.ProtocolError: ('Connection aborted.', BadStatusLine('\r\n',))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "arjun.py", line 237, in
finalResult[url] = initialize(url, include, headers, GET, delay, paramList, threadCount)
File "arjun.py", line 211, in initialize
toBeChecked = narrower(toBeChecked, url, include, headers, GET, delay, originalResponse, originalCode, reflections, factors, threadCount)
File "arjun.py", line 161, in narrower
if result.result():
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
return self.__get_result()
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "arjun.py", line 134, in quickBruter
newResponse = requester(url, joined, headers, GET, delay)
File "/root/Arjun/core/requester.py", line 20, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/lib/python3/dist-packages/requests/api.py", line 72, in get
return request('get', url, params=params, **kwargs)
File "/usr/lib/python3/dist-packages/requests/api.py", line 58, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 520, in request
resp = self.send(prep, **send_kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 630, in send
r = adapter.send(request, **kwargs)
File "/usr/lib/python3/dist-packages/requests/adapters.py", line 490, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('\r\n',))
On line 91 you have forgot the ()
Can you add slack notification in your repo
For known query parameters, test if the application behavior changes when different capitalization rules are applied to the variable name, i.e. if the parameter is redirect_uri, then check how the application responds to: REDIRECT_URI, Redirect_uri. If the app response doesn't change, then remove in-memory parameter database entries (loaded from param.txt
) to reduce required protocol data transmissions.
Similarly, provision a separate fingerprinting process to find any stripped characters classes. In other words, test redirect_uri and redirecturi individually. If they act identical, all in-memory param DB entries with underscores can be removed.
Hi @s0md3v,
Using your last version, I had this stracktrace.
_
/_| _ '
( |/ /(//) v1.6
_/
[~] Analysing the content of the webpage
[~] Analysing behaviour for a non-existent parameter
[!] Reflections: 0
[!] Response Code: 200
[!] Content Length: 21174
[!] Plain-text Length: 8487
[~] Parsing webpage for potential parameters
Traceback (most recent call last):
File "./arjun.py", line 237, in <module>
finalResult[url] = initialize(url, include, headers, GET, delay, paramList, threadCount)
File "./arjun.py", line 200, in initialize
heuristic(firstResponse.text, paramList)
File "./arjun.py", line 122, in heuristic
print('%s Heuristic found a potential %s parameter: %s%s%s' % (good, method.group(1), green, inpName, end))
AttributeError: 'NoneType' object has no attribute 'group'
I didn't take the time to do a good PR, I'm sorry.
However, the problem occurs here (because method
is None) so the print fails.
Thanks a lot for all your projects and work, I which you a happy new year ;)
Hello,
This is more of a request than "bug", but it would be great if i could feed in a target file without specifying http/s and the script should automatically try to connect over both unless the user specifics otherwise. A user should be able to specify a flag like "--assume-http" or "--assume-https" to have the script try http/s respectively.
In some cases websites will end up redirecting multiple times, for which Python has an internal limit and raises an exception. Because this is not handled, it would forcily exit and stop the execution of arjun especially if running it with a big URLs list, example:
/arjun $ python3 ~/Tools/Arjun/arjun.py -f ./u_params.txt -t 50 -o arjun_url_params_report.txt --urls ../all_urls.txt --get
[~] Scanning: http://REMOVED.COM
[~] Analysing the content of the webpage
Traceback (most recent call last):
File "/root/Tools/Arjun/arjun.py", line 246, in <module>
finalResult[url] = initialize(url, include, headers, GET, delay, list(paramList), threadCount)
File "/root/Tools/Arjun/arjun.py", line 172, in initialize
firstResponse = requester(url, include, headers, GET, delay)
File "/root/Tools/Arjun/core/requester.py", line 20, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/local/lib/python3.7/dist-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 668, in send
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 668, in <listcomp>
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 165, in resolve_redirects
raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp)
requests.exceptions.TooManyRedirects: Exceeded 30 redirects.
I was able to quickly patch this by editing how GET requests are performed in the requester.py
file and have it retry without following redirections if it fails:
/Arjun/core/requester.py
if GET:
try:
response = requests.get(url, params=data, headers=headers, verify=False)
except:
response = requests.get(url, params=data, headers=headers, verify=False, allow_redirects=False)
This is probably not an ideal solution and that is why I did not open a merge request, but I am leaving this here as a temporary solution until this is patched.
Thanks for your work on this.
I ran into a case where some missing parameters were present in the response.
Reponse:
Request malformed: templates parameter not found
It would be nice if the response could be parsed to extract words.
I did a quick hack that worked for my case:
def heuristic(response, paramList):
soup = BeautifulSoup(response, 'html.parser')
text = soup.find_all(text=True)[0].split(' ')
done = []
for t in text:
inpName = t
if inpName not in done:
if inpName in paramList:
paramList.remove(inpName)
done.append(inpName)
paramList.insert(0, inpName)
log('%s Heuristic found a potential parameter: %s%s%s' % (good, green, inpName, end))
log('%s Prioritizing it' % good)
Result:
Analysing the content of the webpage
Analysing behaviour for a non-existent parameter
Reflections: 0
Response Code: 400
Content Length: 48
Plain-text Length: 48
Parsing webpage for potential parameters
Heuristic found a potential parameter: Request
Prioritizing it
Heuristic found a potential parameter: malformed:
Prioritizing it
Heuristic found a potential parameter: templates
Prioritizing it
Heuristic found a potential parameter: parameter
Prioritizing it
Heuristic found a potential parameter: not
Prioritizing it
Heuristic found a potential parameter: found
Prioritizing it
Performing heuristic level checks
Heuristic found 6 potential parameters.
Scan Completed
Valid parameter found: templates
like
py -3 arjun.py -u https://www.baidu.com --get -o result.json
Scan Completed
Valid parameter found: ip
Reason: Different content length
Saving output to JSON file in result.json
How to use the result?
Windows 10 x64
python 3.7
command: python3 arjun.py --headers
Error happens:
`
_
/_| _ '
( |/ /(//) v1.0
_/
Traceback (most recent call last):
File "arjun.py", line 53, in
headers = extractHeaders(prompt())
File "D:\Download\Arjun-master\Arjun-master\core\prompt.py", line 11, in prompt
child_pid = os.fork()
AttributeError: module 'os' has no attribute 'fork'
`
suggested use --headers '{"key": "value"}'
get arguments, not prompt
When a new parameter name is discovered, test for its existence in the opposite HTTP method (i.e. if it was found in GET, try POST or vice versa.) Optionally test other RFC-specified and/or user-defined HTTP verbs..
Hello,
I'm trying to bruteforce GET parameters of an endpoint that requires a key for authentication. The value is an ID that is base64 encoded:
This is the command I'm using:
python arjun.py -u https://taget.com/endpoint --get --include 'key=MTIzNDU2Nzg5MDEyMzQ%3D'
The problem is that the "%" is URL encoded again when doing the request.
To solve this I used this solution:
def requester(url, data, headers, GET, delay):
data = "&".join("%s=%s" % (k,v) for k,v in data.items())
Source: https://stackoverflow.com/a/23497912
Thanks for the tool !
As mentioned in the working of Arjun point 2, A second request to the URL is made but this time, a randomly generated parameter, this means that Arjun currently sends a random value with each parameter to identify any change in response.
But sometimes there can be other valid params that are missed by this approach.
Params like debug=1, admin=true, admin=yes.
More params are mentioned here: https://github.com/yehgdotnet/data-payloads/blob/master/params/debug_params.txt
It would be beneficial if with the current (-f) option of passing wordlist, we can pass a wordlist like the one above and Arjun can decide a valid param by looking at the change in response which Arjun is already capable of doing.
I don't get, what Arjun does?
From time to time, I get the following error and the program quits. It has occurred a couple of times over the past weeks. It seems it does not depend on a specific url because I tested one url a couple of times and one time arjun runs without problems and next time it throws this error.
Traceback (most recent call last): File "arjun.py", line 267, in <module> finalResult[url] = initialize(url, include, headers, GET, delay, list(paramList), threadCount) File "arjun.py", line 211, in initialize toBeChecked = narrower(toBeChecked, url, include, headers, GET, delay, originalResponse, originalCode, reflections, factors, threadCount) File "arjun.py", line 161, in narrower if result.result(): File "/usr/lib/python3.7/concurrent/futures/_base.py", line 428, in result return self.__get_result() File "/usr/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result raise self._exception File "/usr/lib/python3.7/concurrent/futures/thread.py", line 57, in run result = self.fn(*self.args, **self.kwargs) File "arjun.py", line 134, in quickBruter newResponse = requester(url, joined, headers, GET, delay) File "/home/tools/arjun/core/requester.py", line 28, in requester return response UnboundLocalError: local variable 'response' referenced before assignment
python3 arjun.py -u <host url> --get -t 1
--post
with the same host url works fine.
Environment:
Python 3.6.5
Ubuntu 18.04
Arjun master branch
[~] Analysing the content of the webpage
[~] Now lets see how target deals with a non-existent parameter
[!] Reflections: 1
[!] Response Code: 200
[!] Content Length: 580905
[!] Plain-text Length: 281201
[~] Parsing webpage for potential parameters
...
[~] Performing heuristic level checks
Traceback (most recent call last):
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 601, in urlopen
chunked=chunked)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 387, in _make_request
six.raise_from(e, None)
File "<string>", line 2, in raise_from
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 383, in _make_request
httplib_response = conn.getresponse()
File "/home/dizcza/anaconda3/lib/python3.6/http/client.py", line 1331, in getresponse
response.begin()
File "/home/dizcza/anaconda3/lib/python3.6/http/client.py", line 297, in begin
version, status, reason = self._read_status()
File "/home/dizcza/anaconda3/lib/python3.6/http/client.py", line 279, in _read_status
raise BadStatusLine(line)
http.client.BadStatusLine: <html>
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/requests/adapters.py", line 449, in send
timeout=timeout
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 639, in urlopen
_stacktrace=sys.exc_info()[2])
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/util/retry.py", line 357, in increment
raise six.reraise(type(error), error, _stacktrace)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/packages/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 601, in urlopen
chunked=chunked)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 387, in _make_request
six.raise_from(e, None)
File "<string>", line 2, in raise_from
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/urllib3/connectionpool.py", line 383, in _make_request
httplib_response = conn.getresponse()
File "/home/dizcza/anaconda3/lib/python3.6/http/client.py", line 1331, in getresponse
response.begin()
File "/home/dizcza/anaconda3/lib/python3.6/http/client.py", line 297, in begin
version, status, reason = self._read_status()
File "/home/dizcza/anaconda3/lib/python3.6/http/client.py", line 279, in _read_status
raise BadStatusLine(line)
urllib3.exceptions.ProtocolError: ('Connection aborted.', BadStatusLine('<html>\r\n',))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "arjun.py", line 183, in <module>
toBeChecked = narrower(toBeChecked)
File "arjun.py", line 174, in narrower
if result.result():
File "/home/dizcza/anaconda3/lib/python3.6/concurrent/futures/_base.py", line 425, in result
return self.__get_result()
File "/home/dizcza/anaconda3/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/home/dizcza/anaconda3/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "arjun.py", line 136, in quickBruter
newResponse = requester(url, joiner(params, include), headers, GET, delay)
File "/home/dizcza/tools/kali/Arjun/core/requester.py", line 17, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/requests/api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/requests/sessions.py", line 524, in request
resp = self.send(prep, **send_kwargs)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/requests/sessions.py", line 637, in send
r = adapter.send(request, **kwargs)
File "/home/dizcza/anaconda3/lib/python3.6/site-packages/requests/adapters.py", line 498, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('<html>\r\n',))
I'm trying to use Arjun but this is coming
Traceback (most recent call last):
File "http://arjun.py", line 26, in
from core.requester import requester
File "/root/Arjun/core/requester.py", line 6, in
import requests
ModuleNotFoundError: No module named 'requests'
BTW I'm using python 3.8.3
Hi,
I found out a case where Arjun wasn't able to detect the parameters for a specific url.
The endpoint looked like this:
http://images.server.com/image
The parameter accepted by the endpoint (threw a 500 error when set to an integer) was id
, but if the parameter was set with a value that wasn't an integer, no difference was detected, therefore was marked as "non existent". So:
http://images.server.com/image?id=pnfpzz
Will result in a default HTTP 400
response, but changing the value to a 1
:
http://images.server.com/image?id=1
Will result in a lovely HTTP 500 error, therefore revealing that the parameter id was being processed.
It'll be great if at least two different payloads where sent for the initial detections, one being an integer, and another one being a random string :)
Such as:
/image?id=pnfpzz
/image?id=1337
This may help Arjun detect these kind of cases where the difference is only triggered when one specific type of value is being sent.
Cheers!
Hi s0md3v!
I was trying to brute force the parameters in a virtual host and noticed that Arjun would always get 503 response code and didn't find anything so I started digging in the code and found that in this line:
Line 18 in 6260fc6
An easy fix would be something like that:
if 'Host' not in headers:
headers['Host'] = re.search(r'https?://([^/]+)', url).group(1)
I hope you fix it, Cheers! ๐
My query:
python3 ~/toys/Arjun/arjun.py -u http://somedomain.com/static/sites/xxxx/header-footer/1.4/js/loader.js -f ~/toys/Arjun/db/params.txt -o arjun_out
The progress was stuck after
[~] Performing heuristic level checks
I'm not the best in Python so I was not sure whats the problem, but I was able to trace the issue back to the "enumerate" function in "narrower"
File "arjun.py", line 26, in
from core.requester import requester
File "/root/Arjun/core/requester.py", line 6, in
import requests
ModuleNotFoundError: No module named 'requests'
every time i got this error .
please look at screenshot https://drive.google.com/file/d/15ivIm_Dfqwstzb_K8zc2lH99_LWMZCWZ/view?usp=sharing
Traceback (most recent call last):
File "arjun.py", line 87, in
except FileNotFoundError:
NameError: name 'FileNotFoundError' is not defined
[~] Analysing the content of the webpage
Traceback (most recent call last):
File "./arjun.py", line 237, in
finalResult[url] = initialize(url, include, headers, GET, delay, paramList, threadCount)
File "./arjun.py", line 172, in initialize
firstResponse = requester(url, include, headers, GET, delay)
File "/root/Desktop/Arjun/core/requester.py", line 20, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/local/lib/python3.8/dist-packages/requests/api.py", line 76, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 530, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 665, in send
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 665, in
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 166, in resolve_redirects
raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
requests.exceptions.TooManyRedirects: Exceeded 30 redirects.
Create command-line options for optionally testing unusual CGI input vectors as I mentioned in a tweet:
https://twitter.com/decalresponds/status/1101813400487620610
support for more HTTP verbs & param syntaxes, i.e. pass query params with script path in POST, as multipart form data, query string arrays, fragment vars, etc.
https://stackoverflow.com/questions/611906/http-post-with-url-query-parameters-good-idea-or-not
POST /cgi-bin/a.cgi?avar=aval
Host: host.dom
Content-Length: 0
Connection: Keep-Alive
User-Agent: MSIE
https://ec.haxx.se/http-multipart.html
POST /cgi-bin/a.cgi
Host: host.dom
Content-Length: X
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="avar"
aval
--AaB03x
https://www.npmjs.com/package/query-string
GET /cgi-bin/a.cgi?avar[]=1&avar[]=2
Host: host.dom
User-Agent: MSIE
Connection: Keep-Alive
Adding additional input vectors as they come to mind in the comment below..
root@zhack-vps:~/Arjun# python3 arjun.py --urls myurl --get -t 22
_
/_| _ '
( |/ /(//) v1.6
_/
[] Scanning: http://domain:80/en/url=] Analysing the content of the webpage
[
Traceback (most recent call last):
File "arjun.py", line 246, in
finalResult[url] = initialize(url, include, headers, GET, delay, list(paramList), threadCount)
File "arjun.py", line 172, in initialize
firstResponse = requester(url, include, headers, GET, delay)
File "/root/Arjun/core/requester.py", line 20, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/local/lib/python3.6/dist-packages/requests/api.py", line 76, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 530, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 665, in send
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 665, in
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 166, in resolve_redirects
raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
requests.exceptions.TooManyRedirects: Exceeded 30 redirects.
Hi Somdev,
Firstly thanks for this amazing project ๐
The calling of requests at https://github.com/s0md3v/Arjun/blob/master/arjun.py#L207
is not handling the ConnectionError exception as result code breaks.
You can reproduce the same using below command:
python3 arjun.py --get -t 80 -u https://pornhub.com
I have fixed the issue by making following changes
import json
import requests
import argparse
+from requests.exceptions import ConnectionError
+
from urllib.parse import unquote
@@ -131,7 +133,7 @@ def quickBruter(params, originalResponse, originalCode, reflections, factors, in
else:
return False
-def bruter(param, originalResponse, originalCode, factors, include, reflections, delay, headers, url, GET):
+def bruter(param, originalResponse, originalCode, factors, include, reflections, delay, headers, url, GET):
fuzz = randomString(6)
data = {param : fuzz}
data.update(include)
@@ -203,11 +205,22 @@ def initialize(url, include, headers, GET, delay, paramList, threadCount):
toBeChecked = slicer(paramList, 50)
foundParams = []
+
while True:
- toBeChecked = narrower(toBeChecked, url, include, headers, GET, delay, originalResponse, originalCode, reflections, factors, threadCount)
- toBeChecked = unityExtracter(toBeChecked, foundParams)
- if not toBeChecked:
- break
+ try:
+ toBeChecked = narrower(toBeChecked, url, include, headers, GET, delay, originalResponse, originalCode, reflections, factors, threadCount)
+ toBeChecked = unityExtracter(toBeChecked, foundParams)
+
+ if not toBeChecked:
+ break
+
+ except ConnectionError as e:
+ log('%s Host is blocking probes! %s%s' % (info, red, end))
+ sys.exit(1)
+
+ except Exception as e:
+ print("Exception :", e.__class__.__name__)
+
if foundParams:
log('%s Heuristic found %i potential parameters.' % (info, len(foundParams)))
It basically catches the ConnectionError exception and exits the program
PS: This is surely not the best approach as it will break the functionality of --urls
flag
--
Regards,
Bugbaba
This is not about any bug/feature, I just want to confirm what I understood after going through the source.
That's all..
Is this the same logic you wrote? I just want to confirm it. Thank you..
Hi Somdev,
An admin can configure the server to restrict number of request made per second by a client to protect itself from dos, bruteforce and similar attacks.
reference: https://httpstatuses.com/429
In those cases, server starts replying with 429 status code and it gets flagged as finding by arjun because of change in status code https://github.com/s0md3v/Arjun/blob/master/arjun.py#L142.
Idealy there should be check made at https://github.com/s0md3v/Arjun/blob/master/arjun.py#L121 if status code is 429 and hence stop further probes.
Possible fix:
def quickBruter(params, originalResponse, originalCode, reflections, factors, include, delay, headers, url, GET):
joined = joiner(params, include)
newResponse = requester(url, joined, headers, GET, delay)
if newResponse.status_code == 429:
log('%s Status: 429 returned! ' % bad)
sys.exit(1)
You can reproduce this by using arjun against https://blog.groove.co/
--
Regards,
Bugbaba
When using the option -o the file is not saved.
I will create a pull request once I fixed the issue
It would be great if you could proxy the requests through another tool to see what is going on and what executed on the endpoint.
It would be a good thing to do since almost all hunters creates there own automation tools for their techniques.
Hello,
Below is the output i received when scanning from a target file. The file had 436 addresses in it.
python3 arjun.py --urls /opt/Sublist3r/example_output.txt -t 25
_
/_| _ '
( |/ /(//) v1.6
_/
[~] Scanning: www.example.com
[~] Analysing the content of the webpage
[~] Analysing behaviour for a non-existent parameter
[!] Reflections: 1
[!] Response Code: 403
[!] Content Length: 386
[!] Plain-text Length: 320
[~] Parsing webpage for potential parameters
[~] Performing heuristic level checks
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/urllib3/contrib/pyopenssl.py", line 453, in wrap_socket
cnx.do_handshake()
File "/usr/lib/python3/dist-packages/OpenSSL/SSL.py", line 1915, in do_handshake
self._raise_ssl_error(self._ssl, result)
File "/usr/lib/python3/dist-packages/OpenSSL/SSL.py", line 1640, in _raise_ssl_error
raise SysCallError(-1, "Unexpected EOF")
OpenSSL.SSL.SysCallError: (-1, 'Unexpected EOF')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/urllib3/connectionpool.py", line 600, in urlopen
chunked=chunked)
File "/usr/local/lib/python3.7/dist-packages/urllib3/connectionpool.py", line 343, in _make_request
self._validate_conn(conn)
File "/usr/local/lib/python3.7/dist-packages/urllib3/connectionpool.py", line 839, in _validate_conn
conn.connect()
File "/usr/local/lib/python3.7/dist-packages/urllib3/connection.py", line 344, in connect
ssl_context=context)
File "/usr/local/lib/python3.7/dist-packages/urllib3/util/ssl_.py", line 344, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/local/lib/python3.7/dist-packages/urllib3/contrib/pyopenssl.py", line 459, in wrap_socket
raise ssl.SSLError('bad handshake: %r' % e)
ssl.SSLError: ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/requests/adapters.py", line 449, in send
timeout=timeout
File "/usr/local/lib/python3.7/dist-packages/urllib3/connectionpool.py", line 638, in urlopen
_stacktrace=sys.exc_info()[2])
File "/usr/local/lib/python3.7/dist-packages/urllib3/util/retry.py", line 398, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='www.example.com', port=443): Max retries exceeded with url: /?ngd=823658&nge=513850&ngf=919109&ngg=556475&ngh=891278&ngi=085140&ngj=787745&ngk=895942&ngl=342566&ngm=593508&ngn=720103&ngo=119027&ngp=324273&ngq=604840&ngr=413635&ngs=807938&ngt=338034&ngu=658367&ngv=472298&ngw=881795&ngx=388182&ngy=773095&ngz=662071&nh=031686&nha=488481&nhb=911366&nhc=074080&nhd=214385&nhe=289926&nhf=655834&nhg=824020&nhh=923376&nhi=526392&nhj=907304&nhk=988527&nhl=402676&nhm=375201&nhn=167334&nho=178807&nhp=748434&nhq=375429&nhr=093398&nhs=666865&nht=858836&nhu=179946&nhv=760124&nhw=643403&nhx=239654&nhy=640813&nhz=203072&ni=599959&nia=705022&nib=770501&nic=824025&nick=710193&NICK=460823&nickname=327265&nid=597483&nID=017862&nie=254182&nif=842625&nig=441040&nih=555816&nii=845795&nij=950130&nik=174546&nil=714814&nim=877190&nin=111767&nio=634869&nip=738078&niq=649969&nir=325424&nis=065515&nit=171309&niu=288568&niv=977452&nivoslider4wp_animSpeed=334654&nivoslider4wp_backgroundCaption=837956&nivoslider4wp_captionOpacity=320037&nivoslider4wp_colorCaption=076423&nivoslider4wp_colsBox=336234&nivoslider4wp_controlNav=968952&nivoslider4wp_directionNav=790816&nivoslider4wp_directionNavHide=940833&nivoslider4wp_effect=474707&nivoslider4wp_file_id=017060&nivoslider4wp_file_text_headline=431210&nivoslider4wp_file_type=411049&nivoslider4wp_height=279929&nivoslider4wp_image_link=063366&nivoslider4wp_imageQuality=903696&nivoslider4wp_js=916074&nivoslider4wp_keyboardNav=276467&nivoslider4wp_manualAdvance=698156&nivoslider4wp_pauseOnHover=556000&nivoslider4wp_pauseTime=782926&nivoslider4wp_rowsBox=817627&nivoslider4wp_width=378919&niw=407163&nix=516910&niy=460171&niz=803962&nj=043810&nja=223492&njb=589457&njc=005415&njd=652608&nje=513282&njf=532035&njfontcolor=592605&njform=115100&njg=933039&njh=641238&nji=646266&njj=835145&njk=514396&njl=347244&njlowercolor=588531&njm=743297&njn=660313&njo=331995&njp=324578&njq=004989&njr=005506&njs=903786&njt=751920&nju=042433&njv=798297&njw=315356 (Caused by SSLError(SSLError("bad handshake: SysCallError(-1, 'Unexpected EOF')")))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "arjun.py", line 245, in <module>
finalResult[url] = initialize(url, include, headers, GET, delay, list(paramList), threadCount)
File "arjun.py", line 210, in initialize
toBeChecked = narrower(toBeChecked, url, include, headers, GET, delay, originalResponse, originalCode, reflections, factors, threadCount)
File "arjun.py", line 160, in narrower
if result.result():
File "/usr/lib/python3.7/concurrent/futures/_base.py", line 428, in result
return self.__get_result()
File "/usr/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/usr/lib/python3.7/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "arjun.py", line 133, in quickBruter
newResponse = requester(url, joined, headers, GET, delay)
File "/opt/Arjun/core/requester.py", line 20, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/local/lib/python3.7/dist-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 524, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 659, in send
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 659, in <listcomp>
history = [resp for resp in gen] if allow_redirects else []
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 238, in resolve_redirects
**adapter_kwargs
File "/usr/local/lib/python3.7/dist-packages/requests/sessions.py", line 637, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/requests/adapters.py", line 514, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='www.example.com', port=443): Max retries exceeded with url: /?ngd=823658&nge=513850&ngf=919109&ngg=556475&ngh=891278&ngi=085140&ngj=787745&ngk=895942&ngl=342566&ngm=593508&ngn=720103&ngo=119027&ngp=324273&ngq=604840&ngr=413635&ngs=807938&ngt=338034&ngu=658367&ngv=472298&ngw=881795&ngx=388182&ngy=773095&ngz=662071&nh=031686&nha=488481&nhb=911366&nhc=074080&nhd=214385&nhe=289926&nhf=655834&nhg=824020&nhh=923376&nhi=526392&nhj=907304&nhk=988527&nhl=402676&nhm=375201&nhn=167334&nho=178807&nhp=748434&nhq=375429&nhr=093398&nhs=666865&nht=858836&nhu=179946&nhv=760124&nhw=643403&nhx=239654&nhy=640813&nhz=203072&ni=599959&nia=705022&nib=770501&nic=824025&nick=710193&NICK=460823&nickname=327265&nid=597483&nID=017862&nie=254182&nif=842625&nig=441040&nih=555816&nii=845795&nij=950130&nik=174546&nil=714814&nim=877190&nin=111767&nio=634869&nip=738078&niq=649969&nir=325424&nis=065515&nit=171309&niu=288568&niv=977452&nivoslider4wp_animSpeed=334654&nivoslider4wp_backgroundCaption=837956&nivoslider4wp_captionOpacity=320037&nivoslider4wp_colorCaption=076423&nivoslider4wp_colsBox=336234&nivoslider4wp_controlNav=968952&nivoslider4wp_directionNav=790816&nivoslider4wp_directionNavHide=940833&nivoslider4wp_effect=474707&nivoslider4wp_file_id=017060&nivoslider4wp_file_text_headline=431210&nivoslider4wp_file_type=411049&nivoslider4wp_height=279929&nivoslider4wp_image_link=063366&nivoslider4wp_imageQuality=903696&nivoslider4wp_js=916074&nivoslider4wp_keyboardNav=276467&nivoslider4wp_manualAdvance=698156&nivoslider4wp_pauseOnHover=556000&nivoslider4wp_pauseTime=782926&nivoslider4wp_rowsBox=817627&nivoslider4wp_width=378919&niw=407163&nix=516910&niy=460171&niz=803962&nj=043810&nja=223492&njb=589457&njc=005415&njd=652608&nje=513282&njf=532035&njfontcolor=592605&njform=115100&njg=933039&njh=641238&nji=646266&njj=835145&njk=514396&njl=347244&njlowercolor=588531&njm=743297&njn=660313&njo=331995&njp=324578&njq=004989&njr=005506&njs=903786&njt=751920&nju=042433&njv=798297&njw=315356 (Caused by SSLError(SSLError("bad handshake: SysCallError(-1, 'Unexpected EOF')")))
There was #22 which you trashed two times. I just wanted to thanks you for wasting my time.
But it's good to see that you take donations now to "encourage me to contribute more to the open source".
To get more traction and make Arjun more attractive for people the installation process must be much simple (here comes PyPI into play) and available through the distributions package repos which doesn't depend on the publishing on PyPI but it a good start for upstream projects to show that they care about the Open Source spirit.
In the current state Arjun is hard to be included in distributions because of the following issues:
setup.py
is missing and setuptools are not usedMost issues were addressed by #22. In #40 I was requested to open an issue additionally to the PR which was closed with no further comment.
For the Fedora Security Lab I have to get the package through the review process and the mentioned points above are blockers (they may not be for other distributions). Those are the first topics which a reviewer will point out.
I started to test a site and provided the url, it started well ut suddenly broke in between. I can't go far with this URL. Works properly another urls. Having an issue with a particular URL only.
_
/_| _ '
( |/ /(//) v1.6
_/
[] Analysing the content of the webpage] Analysing behaviour for a non-existent parameter
[
[!] Reflections: 0
[!] Response Code: 200
[!] Content Length: 31636
[!] Plain-text Length: 13785
[] Parsing webpage for potential parameters] Performing heuristic level checks
[+] Heuristic found a potential GET parameter: collectionId
[!] Prioritizing it
[+] Heuristic found a potential GET parameter: query
[!] Prioritizing it
[+] Heuristic found a potential GET parameter: name
[!] Prioritizing it
[+] Heuristic found a potential GET parameter: email
[!] Prioritizing it
[+] Heuristic found a potential GET parameter: phone
[!] Prioritizing it
[+] Heuristic found a potential GET parameter: subject
[!] Prioritizing it
[+] Heuristic found a potential GET parameter: upload
[!] Prioritizing it
[+] Heuristic found a potential parameter: e
[!] Prioritizing it
[+] Heuristic found a potential parameter: e
[!] Prioritizing it
[+] Heuristic found a potential parameter: g
[!] Prioritizing it
[
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 543, in _updat e_chunk_length
self.chunk_left = int(line, 16)
ValueError: invalid literal for int() with base 16: b'\r\n'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 302, in _error _catcher
yield
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 598, in read_c hunked
self._update_chunk_length()
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 547, in _updat e_chunk_length
raise httplib.IncompleteRead(line)
http.client.IncompleteRead: IncompleteRead(2 bytes read)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/requests/models.py", line 745, in generat e
for chunk in self.raw.stream(chunk_size, decode_content=True):
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 432, in stream
for line in self.read_chunked(amt, decode_content=decode_content):
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 626, in read_c hunked
self._original_response.close()
File "/usr/lib/python3.6/contextlib.py", line 99, in exit
self.gen.throw(type, value, traceback)
File "/usr/lib/python3/dist-packages/urllib3/response.py", line 320, in _error _catcher
raise ProtocolError('Connection broken: %r' % e, e)
urllib3.exceptions.ProtocolError: ('Connection broken: IncompleteRead(2 bytes re ad)', IncompleteRead(2 bytes read))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "arjun.py", line 237, in
finalResult[url] = initialize(url, include, headers, GET, delay, paramList, threadCount)
File "arjun.py", line 211, in initialize
toBeChecked = narrower(toBeChecked, url, include, headers, GET, delay, origi nalResponse, originalCode, reflections, factors, threadCount)
File "arjun.py", line 161, in narrower
if result.result():
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
return self.__get_result()
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_resu lt
raise self._exception
File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "arjun.py", line 134, in quickBruter
newResponse = requester(url, joined, headers, GET, delay)
File "/root/tools/Arjun/core/requester.py", line 20, in requester
response = requests.get(url, params=data, headers=headers, verify=False)
File "/usr/lib/python3/dist-packages/requests/api.py", line 72, in get
return request('get', url, params=params, **kwargs)
File "/usr/lib/python3/dist-packages/requests/api.py", line 58, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 520, in reque st
resp = self.send(prep, **send_kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 670, in send
r.content
File "/usr/lib/python3/dist-packages/requests/models.py", line 823, in content
self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes ()
File "/usr/lib/python3/dist-packages/requests/models.py", line 748, in generat e
raise ChunkedEncodingError(e)
requests.exceptions.ChunkedEncodingError: ('Connection broken: IncompleteRead(2 bytes read)', IncompleteRead(2 bytes read))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.