Attached is an updated versions which fixes several bugs, massively expands 
the directory list, and now checks for robots.txt and adds any directories 
found to the test list (thanks to Michael Scheidell for the suggestion). 

If you would like to help contribute and have administrative access to one or 
more web sites, try running this script against it and report any 
common/developer/backend directories which it didn't find. This includes the 
default or common name of directories which contain certain web applications 
(phpMyAdmin, shopping cart software, etc).

-HD

##
#   This plugin was written by H D Moore <[EMAIL PROTECTED]>
##


if(description)
{
        # script_id(????);
        script_version ("$Revision: 1.1 $");
 
        name["english"] = "Directory Scanner";
        script_name(english:name["english"]);
 
        desc["english"] = "None Yet";

        script_description(english:desc["english"]);
        summary["english"] = "Directory Scanner";
        script_summary(english:summary["english"]);
        script_category(ACT_GATHER_INFO);
        script_copyright(english:"This script is Copyright (C) 2002 Digital Defense 
Inc.");
        family["english"] = "Misc.";
        script_family(english:family["english"]);
        script_dependencie("find_service.nes");
        script_require_ports(80);
        exit(0);
}

function check_req (port, url)
{
    soc = open_sock_tcp(port);
    if (!soc) return (0);
    req = http_get(item:url, port:port);
    send(socket:soc, data:req);
    http_resp = recv(socket:soc, length:16384);
    close (soc);
    return(string(http_resp));    
}

function check_dir_list (dir)
{
    for (CDC=0; dirs[CDC]; CDC=CDC+1)
    {
        if (dirs[CDC] == dir)
        {
            return(1);
        }
    }
    return(0);
}

dirs[0] = ".cobalt";
dirs[1] = "1";
dirs[2] = "10";
dirs[3] = "2";
dirs[4] = "3";
dirs[5] = "4";
dirs[6] = "5";
dirs[7] = "6";
dirs[8] = "7";
dirs[9] = "8";
dirs[10] = "9";
dirs[11] = "AdminWeb";
dirs[12] = "Admin_files";
dirs[13] = "Administration";
dirs[14] = "CVS";
dirs[15] = "DMR";
dirs[16] = "HB";
dirs[17] = "HBTemplates";
dirs[18] = "Images";
dirs[19] = "Log";
dirs[20] = "Mail";
dirs[21] = "PDG_Cart";
dirs[22] = "Stats";
dirs[23] = "StoreDB";
dirs[24] = "Templates";
dirs[25] = "WebShop";
dirs[26] = "WebTrend";
dirs[27] = "Web_store";
dirs[28] = "_backup";
dirs[29] = "_notes";
dirs[30] = "_old";
dirs[31] = "_private";
dirs[32] = "a";
dirs[33] = "access";
dirs[34] = "account";
dirs[35] = "accounting";
dirs[36] = "adm";
dirs[37] = "admin";
dirs[38] = "admin-bak";
dirs[39] = "admin-old";
dirs[40] = "admin.back";
dirs[41] = "administration";
dirs[42] = "administrator";
dirs[43] = "adminweb";
dirs[44] = "analog";
dirs[45] = "app";
dirs[46] = "applets";
dirs[47] = "apps";
dirs[48] = "archive";
dirs[49] = "archives";
dirs[50] = "asp";
dirs[51] = "atc";
dirs[52] = "b";
dirs[53] = "back";
dirs[54] = "backend";
dirs[55] = "backup";
dirs[56] = "bak";
dirs[57] = "bank";
dirs[58] = "banner";
dirs[59] = "beta";
dirs[60] = "billpay";
dirs[61] = "bin";
dirs[62] = "buy";
dirs[63] = "buynow";
dirs[64] = "c";
dirs[65] = "cache-stats";
dirs[66] = "cart";
dirs[67] = "ccard";
dirs[68] = "ccards";
dirs[69] = "cgi";
dirs[70] = "cgi-bin";
dirs[71] = "cgi-csc";
dirs[72] = "cgi-local";
dirs[73] = "cgi-win";
dirs[74] = "cgis";
dirs[75] = "config";
dirs[76] = "core";
dirs[77] = "counter";
dirs[78] = "credit";
dirs[79] = "cron";
dirs[80] = "crons";
dirs[81] = "css";
dirs[82] = "customers";
dirs[83] = "cvsweb";
dirs[84] = "d";
dirs[85] = "dat";
dirs[86] = "data";
dirs[87] = "database";
dirs[88] = "db";
dirs[89] = "dbase";
dirs[90] = "design";
dirs[91] = "dev";
dirs[92] = "devel";
dirs[93] = "development";
dirs[94] = "dl";
dirs[95] = "doc";
dirs[96] = "doc-html";
dirs[97] = "docs";
dirs[98] = "down";
dirs[99] = "download";
dirs[100] = "downloads";
dirs[101] = "dump";
dirs[102] = "e";
dirs[103] = "eforum";
dirs[104] = "email";
dirs[105] = "emailclass";
dirs[106] = "employees";
dirs[107] = "empoyees";
dirs[108] = "error";
dirs[109] = "estmt";
dirs[110] = "etc";
dirs[111] = "exc";
dirs[112] = "exchange";
dirs[113] = "exe";
dirs[114] = "f";
dirs[115] = "file";
dirs[116] = "files";
dirs[117] = "forms";
dirs[118] = "forum";
dirs[119] = "fpadmin";
dirs[120] = "ftp";
dirs[121] = "g";
dirs[122] = "guestbook";
dirs[123] = "guests";
dirs[124] = "hidden";
dirs[125] = "hide";
dirs[126] = "hit_tracker";
dirs[127] = "hitmatic";
dirs[128] = "home";
dirs[129] = "htbin";
dirs[130] = "htdocs";
dirs[131] = "html";
dirs[132] = "ibank";
dirs[133] = "ibill";
dirs[134] = "icons";
dirs[135] = "idea";
dirs[136] = "ideas";
dirs[137] = "imagery";
dirs[138] = "img";
dirs[139] = "imp";
dirs[140] = "import";
dirs[141] = "inc";
dirs[142] = "include";
dirs[143] = "includes";
dirs[144] = "incoming";
dirs[145] = "info";
dirs[146] = "install";
dirs[147] = "intranet";
dirs[148] = "java";
dirs[149] = "jave";
dirs[150] = "jdbc";
dirs[151] = "js";
dirs[152] = "lib";
dirs[153] = "libraries";
dirs[154] = "library";
dirs[155] = "links";
dirs[156] = "loader";
dirs[157] = "log";
dirs[158] = "logfile";
dirs[159] = "logfiles";
dirs[160] = "logger";
dirs[161] = "logging";
dirs[162] = "login";
dirs[163] = "logon";
dirs[164] = "logs";
dirs[165] = "mail";
dirs[166] = "mall_log_files";
dirs[167] = "manual";
dirs[168] = "marketing";
dirs[169] = "msql";
dirs[170] = "mysql";
dirs[171] = "new";
dirs[172] = "odbc";
dirs[173] = "old";
dirs[174] = "oracle";
dirs[175] = "order";
dirs[176] = "orders";
dirs[177] = "outgoing";
dirs[178] = "pages";
dirs[179] = "passport";
dirs[180] = "password";
dirs[181] = "passwords";
dirs[182] = "perl";
dirs[183] = "perl5";
dirs[184] = "phorum";
dirs[185] = "php";
dirs[186] = "phpMyAdmin";
dirs[187] = "poll";
dirs[188] = "polls";
dirs[189] = "postgres";
dirs[190] = "priv";
dirs[191] = "private";
dirs[192] = "prv";
dirs[193] = "pub";
dirs[194] = "public";
dirs[195] = "purchase";
dirs[196] = "purchases";
dirs[197] = "pw";
dirs[198] = "rdp";
dirs[199] = "register";
dirs[200] = "registered";
dirs[201] = "reports";
dirs[202] = "reseller";
dirs[203] = "restricted";
dirs[204] = "retail";
dirs[205] = "root";
dirs[206] = "sales";
dirs[207] = "script";
dirs[208] = "scripts";
dirs[209] = "search";
dirs[210] = "secret";
dirs[211] = "sell";
dirs[212] = "setup";
dirs[213] = "shop";
dirs[214] = "shopper";
dirs[215] = "site";
dirs[216] = "software";
dirs[217] = "source";
dirs[218] = "sql";
dirs[219] = "staff";
dirs[220] = "stat";
dirs[221] = "statistic";
dirs[222] = "statistics";
dirs[223] = "stats";
dirs[224] = "status";
dirs[225] = "store";
dirs[226] = "support";
dirs[227] = "sys";
dirs[228] = "system";
dirs[229] = "tech";
dirs[230] = "temp";
dirs[231] = "templates";
dirs[232] = "test";
dirs[233] = "test-cgi";
dirs[234] = "testing";
dirs[235] = "tmp";
dirs[236] = "tools";
dirs[237] = "trafficlog";
dirs[238] = "tree";
dirs[239] = "updates";
dirs[240] = "usage";
dirs[241] = "user";
dirs[242] = "users";
dirs[243] = "usr";
dirs[244] = "ustats";
dirs[245] = "vfs";
dirs[246] = "web";
dirs[247] = "web800fo";
dirs[248] = "webadmin";
dirs[249] = "webboard";
dirs[250] = "webcart";
dirs[251] = "webcart-lite";
dirs[252] = "webdata";
dirs[253] = "webimages";
dirs[254] = "webimages2";
dirs[255] = "weblog";
dirs[256] = "weblogs";
dirs[257] = "website";
dirs[258] = "webstats";
dirs[259] = "wstats";
dirs[260] = "wusage";
dirs[261] = "www";
dirs[262] = "www-sql";
dirs[263] = "wwwjoin";
dirs[264] = "wwwlog";
dirs[265] = "wwwstats";
dirs[266] = "zipfiles";

# this needs to be updated to match the above list
dirs_last = 266;

# these are the strings used by the 404 checks
errmsg[0] = "not found";
errmsg[1] = "404";
errmsg[2] = "error has occurred";
errmsg[3] = "firewall-1 message";
errmsg[4] = "Reload acp_userinfo database";
errmsg[5] = "IMail Server Web Messaging";
errmsg[6] = "HP Web JetAdmin";
errmsg[7] = "Error processing SSI file";
errmsg[8] = "ExtendNet DX Configuration";
errmsg[9] = "not available";
errmsg[10] = "Content-Length: 0";


debug = 1;


if(debug) display("\n::[ DDI Directory Scanner running in debug mode\n::\n");

report = string("The following directories were discovered: ");
found = 0;

authreport = string("The following directories require authentication: ");
authfound = 0;

fake404 = string("");
Check200 = 1;
Check401 = 1;
Check403 = 1;

port = get_kb_item("Services/www");
if(!port)port = 80;
if(!get_port_state(port))
{
    if(debug) display(":: Error: port ", port, " was not open on target.\n");
    exit(0);
}

##
# pull the robots.txt file
##

soc = open_sock_tcp(port);
if (!soc)
{
    if(debug) display(":: Error: could not connect to port ", port, " on target.\n");
    exit(0);
}

display(":: Checking for robots.txt...\n");
req = http_get(item:"/robots.txt", port:port);
send(socket:soc, data:req);
http_line = recv_line(socket:soc, length:16384);

if (ereg(pattern:"HTTP/1.[01] 200", string:http_line))
{
    while (http_line = recv_line(socket:soc, length:16384))
    {
        if (ereg(pattern:"disallow:", string:http_line, icase:TRUE))
        {
            # yes, i suck at regex's in nasl. I want my \s+!
            robot_dir = ereg_replace(pattern:"disallow:\W*/(.*)/\W$", 
string:http_line, replace:"\1", icase:TRUE); 
            if (debug) display(":: Discovered directory through robots.txt: '", 
robot_dir, "'\n");
            
            if (! check_dir_list(dir:robot_dir))
            {
                # add directory to the list
                dirs_last = dirs_last + 1;
                dirs[dirs_last] = robot_dir;
                if (debug) display(":: Directory '", robot_dir, "' added to test 
list\n");
            } else {
                if (debug) display(":: Directory '", robot_dir, "' already exists in 
test list\n");
            }
        }
    }
}
close(soc);

##
# test for servers which return 200/403/401 for everything
##
soc = open_sock_tcp(port);
if (!soc)
{
    if(debug) display(":: Error: could not connect to port ", port, " on target.\n");
    exit(0);
}

display(":: Checking for non-standard server responses\n");
req = http_get(item:"/NonExistent/", port:port);
send(socket:soc, data:req);
http_resp = recv(socket:soc, length:16384);
close (soc);


if(ereg(pattern:"HTTP/1.[01] 200", string: http_resp))
{
    fake404 = 0;
    
    if(debug) display(":: This server returns 200 for non-existent directories.\n");
    for(i=0;errmsg[i];i=i+1)
    {
        if (ereg(pattern:errmsg[i], string:http_resp, icase:TRUE))
        {
            fake404 = errmsg[i];
            if(debug) display(":: Using '", fake404, "' as an indication of a 404 
error\n");
        }
    }
    
    if (!fake404)
    {
        if(debug) display(":: Could not find an error string to match against for the 
fake 404 response.\n");
        if(debug) display(":: Checks which rely on 200 responses are being 
disabled\n");
        Check200 = 0;
    }
} else {
    fake404 = string("BadString0987654321*DDI*");
}

if(ereg(pattern:"HTTP/1.[01] 401", string: http_resp))
{
    if(debug) display(":: This server requires authentication for non-existent 
directories, disabling 401 checks.\n");
    Check401 = 0;
}

if(ereg(pattern:"HTTP/1.[01] 403", string: http_resp))
{
    if(debug) display(":: This server returns a 403 for non-existent directories, 
disabling 403 checks.\n");
    Check403 = 0;
}


display(":: Starting the directory scan...\n");
for(i=0;dirs[i];i=i+1)
{    
    res = check_req(port:port, url:string("/", dirs[i], "/"));
    
    if( Check200 && 
        ereg(pattern:"HTTP/1.[01] 200", string:res) &&
        ! (ereg(pattern:fake404, string:res, icase:TRUE))
      )
    {
        if(debug) display(":: Discovered: " , dirs[i], "\n");
        
        dir_cache[dir_count] = robot_dir;
        dir_count = dir_count + 1;
        
        if(found)
        {
            report = report + ", /" + dirs[i];
            found=found+1;
        } else {
            report = report + "/" + dirs[i];
        }
        found=found+1;
    }
    
    if(Check403 && ereg(pattern:"HTTP/1.[01] 403", string: res))
    {
        
        if (debug) display(":: Got a 403 for ", dirs[i], ", checking for file in the 
directory...\n");
        
        res2 = check_req(port:port, url:string("/", dirs[i], "/NonExistent.html"));

        if(ereg(pattern:"HTTP/1.[01] 403", string:res2))
        {
            # the whole directory appears to be protected 
            if (debug) display("::   403 applies to the entire directory \n");   
        } else {
            if (debug) display("::   403 applies to just directory indexes \n");
            
            # the directory just has indexes turned off
            if(debug) display(":: Discovered: " , dirs[i], "\n");
            if(found)
            {
                report = report + ", /" + dirs[i];
            } else {
                report = report + "/" + dirs[i];
            }
            found=found+1;            
        }
    }
    
    if(Check401 && ereg(pattern:"HTTP/1.[01] 401", string: res))
    {
        
        if (debug) display(":: Got a 401 for ", dirs[i], "\n");
        if(found)
        {
            authreport = authreport + ", /" + dirs[i];
        } else {
            authreport = authreport + "/" + dirs[i];
        }
        authfound=authfound+1;            
    }    
}

result = string("");

if (found)
{
    result = report;
}

if (authfound)
{
    result = result + string("\n", authreport);
}

if (strlen(result))
{
    security_note(port:port, data:result);
}

Reply via email to