<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://albocoder.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://albocoder.github.io/" rel="alternate" type="text/html" /><updated>2025-08-12T03:30:11+00:00</updated><id>https://albocoder.github.io/feed.xml</id><title type="html">Erin Avllazagaj</title><subtitle></subtitle><author><name>Erin Avllazagaj</name></author><entry><title type="html">My thoughts on Vietnam</title><link href="https://albocoder.github.io/travel/2025/07/23/VietnamTrip.html" rel="alternate" type="text/html" title="My thoughts on Vietnam" /><published>2025-07-23T00:00:00+00:00</published><updated>2025-07-23T00:00:00+00:00</updated><id>https://albocoder.github.io/travel/2025/07/23/VietnamTrip</id><content type="html" xml:base="https://albocoder.github.io/travel/2025/07/23/VietnamTrip.html"><![CDATA[<p><a href="/assets/blog6_vietnam_trip/1abaa2d5-d051-4719-9f57-970d7f0b8961.jpg"><img src="/assets/blog6_vietnam_trip/1abaa2d5-d051-4719-9f57-970d7f0b8961.jpg" alt="Cover photo depicting the turtle lake of Hanoi and a traditional citizen" /></a></p>

<h1 id="introduction">Introduction</h1>

<p>I haven’t posted in a while and I’m intending to write more often. I will start this new era with a non-techincal blog post about my life. In the beginning of May 2025 I went to Vietnam to visit the place my lovely girlfriend was born and raised. I met her amazing family and felt the most welcome I’ve ever been. So the best way to do them justice to my 10-day escapade is to write down my impressions and keep them for eternity in the data pit that is internet (probably will be used to train some AI). I will be detailing my experience in Vietnam through multiple lenses such as: the food, the life, the history and the nature. At last, I will be providing my opinion on my experience and some tips on how to best experience Vietnam if you happen to visit it.</p>

<h1 id="the-welcome">The Welcome</h1>

<p>After a long 2-connection flight to Vietnam I got greeted by <a href="#fig1">this</a>, a warm slightly exaggerated welcome by Jamie (aka. Ngoc) and her brother Trong.</p>

<figure class="image" id="fig1">
    <a href="/assets/blog6_vietnam_trip/20250511_123006(0).jpg"><img src="/assets/blog6_vietnam_trip/20250511_123006(0).jpg" alt="The first welcome at the Noi Bai International Airport." /></a>
    <figcaption style="text-align: center;"><small>Figure 1: The first welcome at the Noi Bai International Airport.</small></figcaption>
</figure>

<p>This will remain my most memorable welcome by far.</p>

<h1 id="the-food">The Food</h1>

<p>I can’t be an experience blog post without a description of the food. After all, “a man’s love is through his stomach”.</p>

<p>The Vietnamese cuisine is defined by its harmonious balance of flavors, textures, and colors. The overarching theme centers around freshness and contrast: crisp herbs, savory broths, and vibrant vegetables are paired with tender meats, seafood, and rice or noodles flavored with variants of fish sauce. Except for the savory broths, most of the food is basically unflavored until you dip it in a sauce.</p>

<p>The fish sauce is the heart and soul of the cuisine, providing that umami and salty concentrated flavor. It forms the foundation for many essential sauces, such as nuoc cham (a tangy dipping sauce for spring rolls and grilled meats), nuoc mam gung (ginger fish sauce for poached chicken), and nuoc mam pha (a blended fish sauce for salads and noodle dishes). These sauces, each with their own blend of lime (or green kumquat), garlic, chili, and sugar, highlight the versatility and central role of fish sauce in Vietnamese cooking. I was surprised when I learned that the fish sauce is not a sauce made for fish food, but made WITH fermented fish.</p>

<figure class="image" id="fig2">
    <a href="/assets/blog6_vietnam_trip/20250511_184654.jpg"><img src="/assets/blog6_vietnam_trip/20250511_184654.jpg" alt="A family meal in &lt;b&gt;Ấm thực Vân Hồ&lt;/b&gt; restaurant (meaning Vân Hồ cuisine) in Hanoi." /></a>
    <figcaption style="text-align: center;"><small>Figure 2: A family meal in <b>Ấm thực Vân Hồ</b> restaurant (meaning Vân Hồ cuisine) in Hanoi.</small></figcaption>
</figure>

<p>Dishes are designed to be shared, encouraging communal eating and conversation. Whether it’s the iconic pho, fragrant bun cha, or simple street-side banh mi, every meal reflects a deep respect for natural ingredients. For example, the photo above shows the first dinner I had with Jamie’s family and relatives. I was greeted and welcomed by everyone. It was especially surprising the way they shared the food with me. It’s considered respectful to pick up food from the shared dishes and drop it on someone’s plate – I was constantly offered prerolled rolls, from the topmost plate in Figure <a href="#fig2">2</a>. That was my favorite dish in this picture – it contained pig ears and some green leaves that where put there to be used as the rolling base (same as the rice paper is for spring rolls). In fact, most of the plates come with these large leaves that are used as the roller base.</p>

<h3 id="bia-ha-noi-and-the-drinking-customs">Bia Ha Noi and the Drinking Customs</h3>

<p>The pride of Hanoi is its crafted beer, Bia Ha Noi. This beer has a mere 4.5% alcohol, but my it managed to hit me at 7 glasses. It tastes similar to a white beer like Heineken, but is less bitter and easier to drink. It’s actually pretty light and it doesn’t get in the way of the flavor of the food.</p>

<p>When it comes to drinking, my impression is that people here gather and enjoy drinks together for hours on end. People rarely drink alone; instead, everyone shares the experience. It’s customary to clink glasses with at least one other person, and if only two people are drinking, they shake hands after each toast. I really liked the idea that no one drinks alone—it truly feels like someone is always there to share the pain, joy, or boredom with you. This theme of social life is quite common in Vietnam, and I really enjoyed it. Even though I don’t look Vietnamese, I was welcomed into this social circle and it made me feel at home.</p>

<h3 id="the-coffee-cà-phê-phin">The coffee: Cà phê phin</h3>

<p>Of course it can’t be a post about Vietnam without mentioning the coffee. Cà phê phin is traditional Vietnamese way of brewing coffee. It is brewed using a small metal drip filter called a phin. Coarsely ground dark-roast coffee—usually robusta—is placed in the filter, hot water is poured over it, and the coffee VERY slowly drips into a cup below. The process takes several minutes and results in a strong, concentrated brew. It’s often served black or mixed with sweetened condensed milk, either hot or iced. This method highlights bold, earthy flavors and delivers a powerful caffeine kick. As a coffee connoiseur (aka. addict) I have to admit this is by far one of the strongest coffees I’ve ever had. Unlike arabica, this one has a more distict sourness. Below is a photo I took while enjoying a “Chari” coffee at the famous <a href="https://www.tripadvisor.com/Attraction_Review-g293924-d2104650-Reviews-or10-Cafe_Mai-Hanoi.html">Ca phe Mai</a> in Hanoi. I ended up buyin $60 worth of coffee from them including the “Paris Mai”, which apparently is pooped out by luwak, a squirrel-like animal.</p>

<figure class="image" id="fig3">
    <a href="/assets/blog6_vietnam_trip/20250519_101244.jpg"><img src="/assets/blog6_vietnam_trip/20250519_101244.jpg" alt="Filter coffee at Ca Phe Mai." /></a>
    <figcaption style="text-align: center;"><small>Figure 3: Filter coffee at Ca Phe Mai.</small></figcaption>
</figure>

<h3 id="my-favorite-dish-bánh-cuốn">My Favorite Dish: Bánh Cuốn</h3>

<p>Bánh cuốn (steamed rice rolls) quickly became the highlight of my culinary journey in Vietnam. For breakfast, Ngoc, Trong, and I decided to ride to a tiny street food shop famous for it. There, the cook skillfully ladled a watery mixture of rice flour onto a thin cloth stretched over a pot of steaming water. The steam, cooked the batter into delicate, translucent rice sheets. These sheets were then filled with a savory blend of minced pork and finely chopped wood ear mushrooms, creating a combination of the squishy rice sheet and the chunky fillings.</p>

<figure class="image" id="fig4">
    <a href="/assets/blog6_vietnam_trip/fav_dish.jpg"><img src="/assets/blog6_vietnam_trip/fav_dish.jpg" alt="Bánh cuốn with pork sausage in sweet&amp;sour sauce." /></a>
    <figcaption style="text-align: center;"><small>Figure 4: Bánh cuốn with pork sausage in sweet&amp;sour sauce.</small></figcaption>
</figure>

<p>The rolls were served with a side of sweet and sour dipping sauce, the same one used on spring rolls, and topped with crisp fried shallots. Each bite was a perfect harmonious dance of crunchy, silky and chunky/squishy textures as well as an elegant balance of sweet, sour, tangy and savory flavors. On the side, we ordered slices of pork sausage dipped in the sauce, complementing each other’s savory flavors. All in all, it was a dish that masterfully mixed flavors and textures beyond what I’ve ever had before.</p>

<h3 id="bún-riêu-crab-noodle-soup">Bún Riêu: Crab Noodle Soup</h3>

<p>This dish is the most colorful and chaotic dish, in my opinion. It’s exactly what its name suggests, AND A LOT MORE. Straight up you will notice a vibrant red broth made from tomatoes and crab paste. The soup is filled with rice noodles, fried tofu, and chunks of crab meat. On top, there are green onions and fried shallots. The broth itself is tangy, slightly sweet, and savory, making it a great comfort food. It was a dish that surprised me with its complexity.</p>

<figure class="image" id="fig5">
    <a href="/assets/blog6_vietnam_trip/20250514_075906.jpg"><img src="/assets/blog6_vietnam_trip/20250514_075906.jpg" alt="Bún Riêu (Crab Noodle Soup) with bread sticks." /></a>
    <figcaption style="text-align: center;"><small>Figure 5: Bún Riêu (Crab Noodle Soup) with bread sticks.</small></figcaption>
</figure>

<p>Most importantly in the figure above you should notice the side dish, fried bread. I’ve had fried bread in USA, but this one tases more crunchy. The way to eat it is to just put it in the soup and let it soak the broth – similar to how we eat regular bread in Albania with some stews.</p>

<h3 id="my-least-favorite-goat-meat">My least favorite: Goat meat</h3>

<p>After our boat trip in Tràng An (where King Kong was filmed), in Figure <a href="#fig6">6</a>, we decided to have lunch in a famous restaurant where they serve goat meat. Unlike Albania, in Vietnam goat is not a common meat, so having a restaurant serve goat meat is rare.</p>

<figure class="image" id="fig6">
    <a href="/assets/blog6_vietnam_trip/20250515_102443.jpg"><img src="/assets/blog6_vietnam_trip/20250515_102443.jpg" alt="Photo of the long boat trip in Tràng An." /></a>
    <figcaption style="text-align: center;"><small>Figure 6: Photo of the long boat trip in Tràng An.</small></figcaption>
</figure>

<p>In the photo below, you can see the final spread of all the dishes we ordered. We went with medium-rare goat meat with lime (the shredded one) and goat meat with fried garlic (the chunkier dish). As is typical, both came with a generous side of fresh plant leaves, which serve as a base to wrap and eat the meat with.</p>

<p>Surprisingly, what stood out the most to me wasn’t the food—it was the tea. We tried kumquat tea and a fruit tea, which I believe was dragonfruit. The fruit tea, in particular, was incredibly refreshing—especially after a day of hiking and exploring the ancient imperial capital of Hoa Lư.</p>

<figure class="image" id="fig7">
    <a href="/assets/blog6_vietnam_trip/20250515_152404.jpg"><img src="/assets/blog6_vietnam_trip/20250515_152404.jpg" alt="Photo of the goat meat, the sides and the teas." /></a>
    <figcaption style="text-align: center;"><small>Figure 7: Photo of the goat meat, the sides and the teas.</small></figcaption>
</figure>

<p>Unfortunately, I have to admit the goat meat was a bit underwhelming. It was quite chewy, and since it was cooked with the skin on, it made things even tougher. Even the more shredded dish was hard to get through. On the bright side, the sides really made up for it—especially the little onions, which were a standout.</p>

<h3 id="the-controversial-bún-đậu-mắm-tôm">The controversial: Bún Đậu Mắm Tôm</h3>

<p>This dish was amazing. It’s ranked #2 in my personal ranking. I really enjoyed the Mắm Tôm (shrimp paste sauce). The is dish made up of rice vermicelli (bún), crispy fried tofu (đậu), and a variety of accompaniments like boiled pork, chả quế (fried cinnamon pork sausage), and of course fermented shrimp paste–marinated intestines. The centerpiece is the shrimp paste. It’s a pungent, salty, deeply umami dipping sauce. The sauce is divisive even among the Vietnamese themselves due to it’s incredibly stron flavor. The sauce is mixed with lime juice, sugar, and chili to balance its intensity – so when you receive it you have to violently mix it until bubbles are formed. I enjoyed it because it was unique and I don’t plan to eat the pork just plainly boiled like that. After that dish Jamie and her brother gave me their seal of approval for becoming a Vietnamese citizen – I’m on the right track.</p>

<figure class="image" id="fig8">
    <a href="/assets/blog6_vietnam_trip/20250519_131204.jpg"><img src="/assets/blog6_vietnam_trip/20250519_131204.jpg" alt="Photo of Mắm Tôm (in that bowl) and Bún Đậu on that large plate on the right." /></a>
    <figcaption style="text-align: center;"><small>Figure 8: Photo of Mắm Tôm (in that bowl) and Bún Đậu on that large plate on the right.</small></figcaption>
</figure>

<h3 id="the-dawg">The dawg</h3>

<p>Staying on the theme of controversy, I’ll now review one of the most unusual dish I tried: dog meat (thịt chó). This traditional specialty, especially common in northern Vietnam, is often grilled and paired with fermented shrimp paste (mắm tôm). The flavor was bold and gamey—reminiscent of goat meat, which is probably why I enjoyed it. My only critique was the chewiness, which stood out even more since I was still partially recovering my sense of taste at the time. In general, Vietnamese cuisine doesn’t seem to shy away from a bit of bite in its meats. Back in Albania, for instance, we tend to pressure-cook goat to make it more tender.</p>

<p>On the ethics of eating dog meat, I don’t side with what I see as selective outrage. I believe all animal life is equal (humans aside), and if someone is comfortable eating an octopus—an animal arguably more intelligent than a dog—it’s hard to justify placing dogs on a higher moral pedestal. Spare me the sentimentality: we are not divine arbiters deciding which species deserve to live and which do not.</p>

<figure class="image" id="fig9">
    <a href="/assets/blog6_vietnam_trip/20250519_201056.jpg"><img src="/assets/blog6_vietnam_trip/20250519_201056.jpg" alt="Photo of the dog meat (Thịt chó)." /></a>
    <figcaption style="text-align: center;"><small>Figure 9: Photo of the dog meat (Thịt chó).</small></figcaption>
</figure>

<h3 id="fertilized-duck-egg-balut">Fertilized duck egg: Balut</h3>

<p>If you thought dog meat was the peak of culinary controversy, meet balut—a fertilized duck egg with the embryo still inside, boiled and eaten straight from the shell. It’s a common street food, but to Western eyes, it’s the kind of thing that makes people clutch their stomach before they’ve even taken a bite. Below is a picture of a 17 day old fertilized duck egg.</p>

<figure class="image" id="fig10">
    <a href="/assets/blog6_vietnam_trip/20250512_112228.jpg"><img src="/assets/blog6_vietnam_trip/20250512_112228.jpg" alt="Photo of the balut." /></a>
    <figcaption style="text-align: center;"><small>Figure 10: Photo of the balut.</small></figcaption>
</figure>

<p>Crack it open and you’re greeted by a mix of textures: tender egg white, rich yolk, and the slightly firmer, delicate crunch of developing bone and beak. Flavor-wise, it’s deeper and more savory than a chicken egg—closer to a meaty broth trapped inside an eggshell. You sprinkle some salt, maybe a dash of vinegar or herbs, and slurp it down while it’s hot.</p>

<p>I liked it. Not in the “oh, it’s weird but I’ll pretend to enjoy it for the story” way, but genuinely liked it. The broth-like richness was addictive, and the mix of textures worked for me. The only reason people recoil is because it forces them to confront the fact that meat comes from living things—something most conveniently forget while buying their sanitized, shrink-wrapped chicken breasts.</p>

<p>As for the morality debates? Please. If you’re okay with eating a full-grown bird, then eating it a few days earlier doesn’t suddenly make you a monster – it arguably spares the bird a lifetime of torture in a cage.</p>

<h3 id="agriculture">Agriculture</h3>

<p>Due to the tropical climate Vietnam bolsters a vast selections of agriculture, many of which are not available in the US or Albania. I had for the first time sugar cane juice (absolutely love it), lychee, mangosteen, jackfruit and even coconut. I really liked mangosteen in particular. It’s a sweet, tangy, and juicy fruit, with hints of peach, strawberry, and citrus.</p>

<figure class="image" id="fig11">
    <a href="/assets/blog6_vietnam_trip/20250513_194557.jpg"><img src="/assets/blog6_vietnam_trip/20250513_194557.jpg" alt="Mangosteen" /></a>
    <figcaption style="text-align: center;"><small>Figure 11: Mangosteen</small></figcaption>
</figure>

<h1 id="the-nature">The nature</h1>

<p>The nature in Vietnam is nothing short of fascinating. I came to realize this when Jamie and I went to Ninh Bình. We booked a boat trip to discover the beauties that attacted the movie producers of <code class="language-plaintext highlighter-rouge">Kong: Skull Island</code>. While gliding along the calm green waters of Tràng An I was surrounded by towering limestone karsts that rose dramatically from the river like ancient sentinels.</p>

<p>Draped in dense greenery, these cliffs created a scene that felt almost otherworldly, their rugged surfaces contrasting beautifully with the lush jungle below. The moody, shifting skies added a mystical atmosphere, casting patterns of light and shadow that danced across the water’s surface.</p>

<figure class="image" id="fig12">
    <a href="/assets/blog6_vietnam_trip/20250515_094531.jpg"><img src="/assets/blog6_vietnam_trip/20250515_094531.jpg" alt="Photo of the dog meat (Thịt chó)." /></a>
    <figcaption style="text-align: center;"><small>Figure 12: Photo of the dog meat (Thịt chó).</small></figcaption>
</figure>

<p>Our small wooden boat, hand-rowed by a skilled local boatwoman, moved quietly through the winding waterways. The gentle splash of the oars was the only sound breaking the tranquil silence. Around us, other boats carried travelers in bright orange life jackets, each person lost in awe at the breathtaking landscape. Tràng An’s peaceful beauty and grand scale made it one of the most unforgettable highlights of my journey through Vietnam.</p>

<p>I want to take a moment to acknowledge the wonderful, welcoming, and deeply underappreciated boatmen and women who work tirelessly to show visitors the natural beauty of Vietnam. Their warmth and generosity left a mark on me. After our tour ended, all the rowers gathered to eat the simple lunches they had brought from home. We joined them with our own snacks, and before long they were offering to share their food and asking about my story. It hit me hard, knowing what I’d learned earlier—that they earn barely 200,000 VND (about $7.60) for a single five-hour trip, and even in high season, they might only get one trip a week. Many are forced to take other jobs just to get by. And yet, despite such modest means, they still sat with a foreigner and insisted on sharing what little they had—proving themselves richer than many in the only currency that truly matters: a heart of gold.</p>

<h1 id="the-history">The history</h1>

<p>When it comes to history, I’ve always been fascinated. Before this trip, though, my knowledge of Vietnamese history was limited to a few YouTube videos and the one-sided perspective of the horrific Vietnam War. Through my dad’s stories, I learned that communist Albania at the time was a strong supporter of Vietnam, with radio broadcasts proudly reporting every achievement of the communist forces against the “imperialist American allies.”</p>

<p>Meanwhile, in the U.S., most of what I learned portrayed the Americans as justified and the Viet Cong as the villains. Given the more recent wars in the Middle East, I’ve come to approach these narratives with a grain of salt. I wanted to learn more about Vietnam’s history before the war to better understand what led up to the conflict, and to hear perspectives on the war from a different point of view.</p>

<h3 id="the-french-colonialism">The French colonialism</h3>

<p>Even before my visit, I learned that the French had a major influence on Vietnam. Many buildings in Hanoi showcase classic French architecture, with features like ornate facades, tall arched windows, wrought-iron balconies, and wide boulevards lined with trees. The French introduced European-style villas, grand government buildings, and cathedrals, which still stand today as striking reminders of that era. Walking through Hanoi’s Old Quarter and French Quarter felt like stepping into a unique blend of Vietnamese and European styles. Colonial buildings now house charming cafés, boutique shops, and small museums, all contributing to the city’s distinct atmosphere.</p>

<p>In fact, French influence goes beyond architecture—some Vietnamese food is also inspired by French cuisine. One of the most famous examples is bánh mì, a crispy baguette sandwich that combines French bread, pate (pork liver) with local ingredients like pickled vegetables, cilantro, chili, and various meats or pâté. Dishes like bò kho (beef stew) and pâté chaud (meat-filled puff pastry) also reflect this fusion. It’s fascinating how colonial history has left such a tangible mark on daily life, not just in the buildings and streets, but in the food people eat and the way the city feels.</p>

<figure class="image" id="fig13">
    <a href="/assets/blog6_vietnam_trip/20250512_152551.jpg"><img src="/assets/blog6_vietnam_trip/20250512_152551.jpg" alt="French-build cathedral in Hanoi." /></a>
    <figcaption style="text-align: center;"><small>Figure 13: French-build cathedral in Hanoi.</small></figcaption>
</figure>

<p>Life under a colonizer isn’t all fun and brioche. In fact, I took my first brush with a grim history lesson from a guided tour of the Hỏa Lò prison in Hanoi (a.k.a. the “Hanoi Hilton”). A small part of this man-made building of horrors is still kept in the heart of Hanoi – reminding the people of the resiliance and strength of their revolutionary freedom fighters.</p>

<figure class="image" id="fig14">
    <a href="/assets/blog6_vietnam_trip/20250517_160539.jpg"><img src="/assets/blog6_vietnam_trip/20250517_160539.jpg" alt="Depiction of an incarceration in the 
Hỏa Lò prison." /></a>
    <figcaption style="text-align: center;"><small>Figure 14: Depiction of an incarceration in the 
Hỏa Lò prison.</small></figcaption>
</figure>

<p>Hỏa Lò Prison was built by the French colonial administration around 1896 in central Hanoi as part of France’s efforts to consolidate control over Indochina. It was the highest security prison in the area – in fact, they mandated that metal used on the door locks must come from France (Fig.<a href="#fig14">14</a>). The French bought the area for dollars on the dime from the residents of Phu Khánh village, which was well-known as a traditional ceramic and clay pot–making community. Known to the French as Maison Centrale, it was one of the largest prisons in French-occupied Southeast Asia, designed to hold up to 450 inmates but often crammed with thousands. Hỏa Lò was primarily used to detain Vietnamese revolutionaries, nationalist intellectuals, and anti-colonial activists, becoming a symbol of French repression and a rallying point for resistance movements seeking independence from colonial rule.</p>

<figure class="image" id="fig15">
    <a href="/assets/blog6_vietnam_trip/20250517_132353.jpg"><img src="/assets/blog6_vietnam_trip/20250517_132353.jpg" alt="A high security steel door in the entrance of a prison block of Hỏa Lò." /></a>
    <figcaption style="text-align: center;"><small>Figure 15: A high security steel door in the entrance of a prison block of Hỏa Lò.</small></figcaption>
</figure>

<p>Many prominent communist revolutionaries were imprisoned at Hỏa Lò during the French colonial era. Ironically, what was meant to break their spirit became a breeding ground for resistance. Inside its grim walls, detainees formed secret study groups to spread Marxist-Leninist ideas, trained one another in political theory, and laid the ideological foundation for Vietnam’s future leadership. Among those held were Trường Chinh, who later became General Secretary of the Communist Party and a key architect of socialist policy in Vietnam, and Nguyễn Văn Cừ, also a Party leader and one of the youngest General Secretaries in its history. Their time in prison was not wasted—it became a period of political consolidation and underground organizing.</p>

<p>Escape stories from Hỏa Lò are legendary and remain a source of pride in Vietnam’s revolutionary narrative. During WW2, when Japan displaced the French colonial administration and occupied Vietnam, the prison system descended into turmoil. As a result, security weakened, and the frequency of successful prison breaks surged. The communist leadership highlights these escapes not just as acts of personal bravery, but as strategic moves that enabled revolutionaries to rejoin the anti-colonial struggle. Today, these stories are preserved as symbols of resilience and the unwavering will for national liberation.</p>

<h3 id="vietnam-war">Vietnam war</h3>

<p>Naturally, no history of Vietnam would be complete without mentioning the civil war between the North and the South. In Hanoi, reminders of that turbulent period are everywhere—museums, monuments, and preserved war relics. One of the most notable sites is the Hỏa Lò Prison, which includes a section dedicated to its role during the war.</p>

<h4 id="the-prison-again">The prison (again)</h4>

<p>After Vietnam was liberated from Japanese occupation, the new socialist government took control of the North. The prison was repurposed to hold American prisoners of war. Conditions were reportedly improved from the harsher state left by the French; prisoners had straw beds, access to games, and basic hygiene supplies. In a place under heavy bombing, concentrating prisoners in one location increased their odds of survival since they wouldn’t bomb a prison. This holds true, especially when many Vietnamese civilians were being bombed indiscriminately in the streets. American POWs famously nicknamed it the “Hanoi Hilton.” While the term was used sarcastically, it’s possible that the prison was statistically safer than being outside, where napalm strikes and friendly fire posed threats to GI’s.</p>

<h4 id="the-b-52-museum">The B-52 museum</h4>

<p>Another spot worth mentioning for its display of war spoils is the B-52 Museum. As the name suggests, it’s dedicated to the American B-52 bomber—an aircraft that struck both fear and deep resentment into the hearts of the Viet Cong. Bringing down one of these ruthless, indiscriminate war machines was a major morale boost for fighters on the front lines. Back in the 1970s, such victories were even broadcast and celebrated over Albanian radio.</p>

<p>What really caught my attention was how both men and women were trained to operate anti-aircraft gun turrets, with many positioned strategically around Hanoi to protect the city. A detail I learned from Jamie’s dad made the picture even grimmer: to evade radar detection, B-52s would dump massive amounts of metal chaff. While intended to confuse tracking systems, those chunks still rained down on homes below, damaging property and sometimes injuring civilians.</p>

<figure class="image" id="fig16">
    <a href="/assets/blog6_vietnam_trip/20250520_160401.jpg"><img src="/assets/blog6_vietnam_trip/20250520_160401.jpg" alt="12.7mm air defense gun turret." /></a>
    <figcaption style="text-align: center;"><small>Figure 16: 12.7mm air defense gun turret.</small></figcaption>
</figure>

<p>Not only did they rely on anti-aircraft gun turrets, but they also deployed Soviet-supplied S-75 Dvina surface-to-air missiles—far more accurate than manual gunfire, but expensive and limited in number. Designed for high-altitude targets, these missiles were the real B-52 killers, able to bring down bombers before they reached their drop zones. Each launch had to count, as wasting one on a decoy wasn’t an option. When a missile did connect, it wasn’t just a tactical win—it was a statement that even America’s most advanced bombers could be blown out of the sky.</p>

<figure class="image" id="fig17">
    <a href="/assets/blog6_vietnam_trip/20250520_155820.jpg"><img src="/assets/blog6_vietnam_trip/20250520_155820.jpg" alt="Soviet air defense rocket." /></a>
    <figcaption style="text-align: center;"><small>Figure 17: Soviet air defense rocket.</small></figcaption>
</figure>

<p>The MiG-21 was one of the North Vietnamese Air Force’s key weapons against B-52 bombers, and it went on to become one of the most produced supersonic aircraft in history. The museum features displays honoring distinguished pilots, complete with their kill counts and the aircraft they intercepted.</p>

<figure class="image" id="fig18">
    <a href="/assets/blog6_vietnam_trip/20250520_161840.jpg"><img src="/assets/blog6_vietnam_trip/20250520_161840.jpg" alt="MiG-21 airplane." /></a>
    <figcaption style="text-align: center;"><small>Figure 18: MiG-21 airplane.</small></figcaption>
</figure>

<p>At the entrance of the museum, visitors are immediately greeted by a striking symbol of conflict—a destroyed B-52 bomber, a powerful reminder of the war’s enduring impact.</p>

<figure class="image" id="fig19">
    <a href="/assets/blog6_vietnam_trip/20250520_155527.jpg"><img src="/assets/blog6_vietnam_trip/20250520_155527.jpg" alt="B-52 bomber's pieces." /></a>
    <figcaption style="text-align: center;"><small>Figure 19: B-52 bomber's pieces.</small></figcaption>
</figure>

<h4 id="uncle-ho-bác-hồ-aka-hồ-chí-minh">Uncle Ho (Bác Hồ) (aka. Hồ Chí Minh)</h4>

<p>Hồ Chí Minh was the figurehead of North Vietnam’s unification war. From what I was told, he was a humble man who rejected the riches and spoils of war in favor of his people—sometimes with a frightening level of efficiency. As I saw for myself, he lived in a modest stilt house built beside a small pond, choosing it over the opulent French colonial governor’s palace just a short walk away. His sandals were cut from worn-out car tires, and his plain khaki suit was worn so often it became part of his public image. He personally read letters sent to him by ordinary citizens, sometimes giving direct orders to solve their problems without waiting for bureaucracy to grind into motion. Even in wartime, he was relentlessly pragmatic—approving strategies that made full use of every available resource, and refusing to waste manpower, supplies, or time, no matter the sentiment attached.</p>

<p>He is still deeply revered in Hanoi today. His body lies preserved in the mausoleum, where visitors file past in silence. After our quiet walk around him, we explored the building where he once worked and met with other leaders. What struck me most was learning that he had maintained good relations with the Albanian government at the time. In Fig. <a href="#fig20">20</a> you can see the meeting room where he hosted these leaders—preserved exactly as it was, frozen in time, much like the man himself.</p>

<figure class="image" id="fig20">
    <a href="/assets/blog6_vietnam_trip/20250514_103549.jpg"><img src="/assets/blog6_vietnam_trip/20250514_103549.jpg" alt="Meeting room of the president." /></a>
    <figcaption style="text-align: center;"><small>Figure 20: Meeting room of the president.</small></figcaption>
</figure>

<p>Even the Vietnamese diaspora in France praised his efforts. As a gesture of appreciation, they presented him with a Peugeot 404.</p>

<figure class="image" id="fig21">
    <a href="/assets/blog6_vietnam_trip/20250514_104351.jpg"><img src="/assets/blog6_vietnam_trip/20250514_104351.jpg" alt="Presidential gift from Vietnamese-French diaspora." /></a>
    <figcaption style="text-align: center;"><small>Figure 21: Presidential gift from Vietnamese-French diaspora.</small></figcaption>
</figure>

<h1 id="conclusion">Conclusion</h1>

<p>Vietnam is a fascinating country rich in history and culture, home to a resilient and enduring people who have overcome countless challenges throughout the centuries and are in an upwards trajectory of economical growth. Their strength and spirit are deeply woven into the fabric of the nation, reflected in everything from vibrant traditions to rapid modernization. I would strongly encourage anyone to visit this country and experience the culture, tradition and the welcoming of the Vietnamese. I have never felt more welcome in a foreign country, where people would sit down and share their food with you just to hear your story and represent their country in the most positive light they personally could. <a href="https://vietnam.gov.vn/tourism-68967">Here</a> are a few more recommendations from the official website.</p>]]></content><author><name>Erin Avllazagaj</name></author><category term="travel" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Finding the slab cache for each object in Linux kernel using static analysis</title><link href="https://albocoder.github.io/exploitation/linux%20kernel/2024/06/09/KernelStaticAnalysis.html" rel="alternate" type="text/html" title="Finding the slab cache for each object in Linux kernel using static analysis" /><published>2024-06-09T00:00:00+00:00</published><updated>2024-06-09T00:00:00+00:00</updated><id>https://albocoder.github.io/exploitation/linux%20kernel/2024/06/09/KernelStaticAnalysis</id><content type="html" xml:base="https://albocoder.github.io/exploitation/linux%20kernel/2024/06/09/KernelStaticAnalysis.html"><![CDATA[<p><a href="/assets/blog5_static_analysis_kernel/cover.jpeg"><img src="/assets/blog5_static_analysis_kernel/cover.jpeg" alt="Analyst detective AI cover photo" /></a></p>

<h1 id="introduction">Introduction</h1>
<p>I’ve seen a lot of papers in the academia talk about static analysis in linux kernel’s source code. Some use old tools like <em>KINT</em>, some others just mention an <em>LLVM</em> use-def pass, but for a newcomer it’s difficult to write all of those on their own. In this blog post, I will go step by step into <strong>how to track a variable allocation, its data type and it respective kmalloc cache</strong>. I will be running everything in <code class="language-plaintext highlighter-rouge">Ubuntu 20.04</code> but it can work in any OS if you try hard enough. For this analysis we will try to infer the kernel cache for each kernel object.</p>

<p>In this mini-project I will be using <code class="language-plaintext highlighter-rouge">clang-13</code> with linux kernel <code class="language-plaintext highlighter-rouge">5.13.14</code> (same kernel as in my other <a href="/exploit/2023/03/13/KernelFileExploit.html" target="_blank">blog post</a> where I exploit <code class="language-plaintext highlighter-rouge">CVE-2017-27666</code>).
<!-- In kernel version 4, the compiler works with clang version 11, but in version `5.13` I was required to use version 13. --></p>

<h1 id="setup">Setup</h1>

<p>We start off with downloading and extracting the linux kernel code as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.13.14.tar.gz
tar -xf linux-5.13.14.tar.gz
rm linux-5.13.14.tar.gz
</code></pre></div></div>

<p>Then we need to download <em>clang-13</em>  specifically release tag <code class="language-plaintext highlighter-rouge">llvmorg-13.0.1</code> from github (check the code below). 
After downloading we patch it in such a way that when we compile the kernel we dump the LLVM bitcode before the optimizations happen (the linux kernel must be compiled with <code class="language-plaintext highlighter-rouge">-O2</code> flag so we can’t turn it off).
This patch allows the compiler to write the bitcode file with extension <code class="language-plaintext highlighter-rouge">.c.bc</code> for each <code class="language-plaintext highlighter-rouge">.c</code> file that is compiled.
Download the patch from <a href="https://gist.github.com/Albocoder/f81771e4a8ea6cce8ec79279eb07d18c">here</a>.
In short, you can <code class="language-plaintext highlighter-rouge">make</code> this clang using the following commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># setup llvm-13
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout llvmorg-13.0.1
git clone https://gist.github.com/f81771e4a8ea6cce8ec79279eb07d18c.git llvmpatch
mv llvmpatch/llvm_write_bitcode_13.patch ./
git apply llvm_write_bitcode_13.patch

# now compile clang in RELEASE mode
cmake -S llvm -B build -G Unix Makefiles -DCMAKE_BUILD_TYPE=Release
cd build
make -j$(nproc)
</code></pre></div></div>

<p>Note that these commands work for Linux. To build this on Windows just read their README from the github <a href="https://github.com/llvm/llvm-project/tree/llvmorg-13.0.1">repo</a>. If you are using TempleOS, unfortunately Terry Davis didn’t consider <code class="language-plaintext highlighter-rouge">llvm-project</code> holy enough to be supported(I made that up).</p>

<h1 id="compiling-the-kernel">Compiling the kernel</h1>

<p>Now that you have clang compiled let’s go back to the Linux kernel directory and compile it (after <code class="language-plaintext highlighter-rouge">make defconfig</code>) using this command line: <code class="language-plaintext highlighter-rouge">make CC=&lt;path to: llvm-project/build/bin/clang&gt; -j$(nproc)</code></p>

<figure class="image" id="fig1">
    <a href="/assets/blog5_static_analysis_kernel/bc_compilation.png"><img src="/assets/blog5_static_analysis_kernel/bc_compilation.png" alt="Generating LLVM bitcode during compilation." /></a>
    <figcaption style="text-align: center;"><small>Figure 1: Generating LLVM bitcode during compilation.</small></figcaption>
</figure>

<p>After compiling you should be getting an output that looks like Figure <a href="#fig1">1</a>. Now in the compiled <code class="language-plaintext highlighter-rouge">kernel</code> directory we have a bunch of files that look like Figure <a href="#fig2">2</a>. As you can see below we have a bunch of <code class="language-plaintext highlighter-rouge">.c.bc</code> files.</p>

<figure class="image" id="fig2">
    <a href="/assets/blog5_static_analysis_kernel/compilation_output.png"><img src="/assets/blog5_static_analysis_kernel/compilation_output.png" alt="Output of the compilation." /></a>
    <figcaption style="text-align: center;"><small>Figure 2: Output of the compilation.</small></figcaption>
</figure>

<h1 id="analyzing-the-kernel">Analyzing the kernel</h1>

<p>As a quick start lets download a modified version of <code class="language-plaintext highlighter-rouge">KINT</code> and compile it. This version is a modified one from the <a href="https://github.com/Markakd/DirtyCred">DirtyCred</a>’s code. First let’s clone the <a href="https://github.com/Albocoder/slab-cache-analyzer">github repo</a> and compile it. The analysis binary is in <code class="language-plaintext highlighter-rouge">./build/lib/analyzer</code>. We feed every single <code class="language-plaintext highlighter-rouge">.c.bc</code> file generated by the compiler as an argument for the <code class="language-plaintext highlighter-rouge">analyzer</code> and run it. To do that we go to <code class="language-plaintext highlighter-rouge">./build/lib/</code> and run this command in a <em>bash</em> shell that supports this weird apostrophe: <code class="language-plaintext highlighter-rouge">./analyzer `find &lt;ROOT OF KERNEL SOURCE&gt; -name "*.c.bc" ` 2&gt; struct_cache_res.txt</code>.
The command first executes the <code class="language-plaintext highlighter-rouge">find</code> in the root of the linux kernel and prints out a list of all files that end with <code class="language-plaintext highlighter-rouge">.c.bc</code>. This list is the fed as parameters to the <code class="language-plaintext highlighter-rouge">analyzer</code> program. The <code class="language-plaintext highlighter-rouge">2&gt;</code> makes both stdout and stderr be written into <code class="language-plaintext highlighter-rouge">struct_cache_res.txt</code> file. This way all the output of the analyzer is stored in a file to be processed later as needed. When we run this command (after removing the file dump part) on the kernel we compiled above we get the results in Figure <a href="#fig3">3</a>.</p>

<figure class="image" id="fig3">
    <a href="/assets/blog5_static_analysis_kernel/analysis_output.png"><img src="/assets/blog5_static_analysis_kernel/analysis_output.png" alt="Output of the static analysis." /></a>
    <figcaption style="text-align: center;"><small>Figure 3: Output of the static analysis.</small></figcaption>
</figure>

<h3 id="explanation">Explanation</h3>

<p>Now that we know we can do this, let’s dive deeper into how it works exactly. Since this code has been repurposed by many other people, it has many variables that are not useful to us. The meat of the analyzer is in <code class="language-plaintext highlighter-rouge">src/lib</code> folder, so go in that directory and open VScode using <code class="language-plaintext highlighter-rouge">code .</code> command. The program starts from <code class="language-plaintext highlighter-rouge">KAMain.cc</code>. In line <em>162</em> the program loops through all the arguments (the <code class="language-plaintext highlighter-rouge">.c.bc</code> file paths), reads the LLVM IR (Intermediate Representation) and parses it into a <a href="https://llvm.org/doxygen/classllvm_1_1Module.html"><code class="language-plaintext highlighter-rouge">LLVM::Module</code></a> object. In the end it runs <code class="language-plaintext highlighter-rouge">doBasicInitialization</code> function that parses all the structures defined in that <em>Module</em> and populates it with info such as the fields/members and the struct size. This will be useful later to determine the cache based on the struct size. The thing that I modified is <code class="language-plaintext highlighter-rouge">printAllStructsAndAllocCaches();</code> from <code class="language-plaintext highlighter-rouge">StructAnalyzer.cc</code>. The new function now calls <code class="language-plaintext highlighter-rouge">getAllocCache()</code> (in <code class="language-plaintext highlighter-rouge">StructAnalyzer.h</code>) for each structure. In Figure <a href="#fig4">4</a> you can see part of that code.</p>

<figure class="image" id="fig4">
    <a href="/assets/blog5_static_analysis_kernel/analyzer_code.png"><img src="/assets/blog5_static_analysis_kernel/analyzer_code.png" alt="The main code that figures out the cache for a struct." /></a>
    <figcaption style="text-align: center;"><small>Figure 4: The main code that figures out the cache for a struct.</small></figcaption>
</figure>

<figure class="image" id="fig5">
    <a href="/assets/blog5_static_analysis_kernel/alloc_functions.png"><img src="/assets/blog5_static_analysis_kernel/alloc_functions.png" alt="The generic and specific allocation functions." /></a>
    <figcaption style="text-align: center;"><small>Figure 5: The generic and specific allocation functions.</small></figcaption>
</figure>

<p>At first(line <em>187</em>), the code loops through all the allocation sites and checks if the function that allocates the structure is <code class="language-plaintext highlighter-rouge">kmalloc</code> or other generic allocation functions (as shown in Figure <a href="#fig5">5</a>). If any of them is not, then it will go into the first argument of the special function and grab the cache. For example <a href="https://elixir.bootlin.com/linux/latest/source/include/linux/slab.h#L528"><code class="language-plaintext highlighter-rouge">kmem_cache_alloc</code></a> takes as the first arguments the pointer to the cache. This pointer is usually a global variable, which is first <code class="language-plaintext highlighter-rouge">NULL</code>. At runtime this global variable will point to a <a href="https://elixir.bootlin.com/linux/latest/source/mm/slab.h#L251"><code class="language-plaintext highlighter-rouge">kmem_cache</code></a> structure, which is created/allocated after calling <a href="https://elixir.bootlin.com/linux/latest/source/include/linux/slab.h#L227"><code class="language-plaintext highlighter-rouge">kmem_cache_create</code></a>.
My code goes to this global variable and checks if its data type is <code class="language-plaintext highlighter-rouge">kmem_cache</code> structure (line <em>218</em>). If it is, then we loop through all of its <code class="language-plaintext highlighter-rouge">users</code> (the LLVM instructions that read or write this global variable) as shown in line <em>219</em> in Figure <a href="#fig5">5</a>. After <code class="language-plaintext highlighter-rouge">kmem_cache_create</code> is called the return value needs to be STOREd into the global variable; not assigned, even though it looks like <code class="language-plaintext highlighter-rouge">global_var = kmem_cache_create(...);</code>. So the code finds the <a href="https://www.llvm.org/doxygen/classllvm_1_1StoreInst.html"><code class="language-plaintext highlighter-rouge">StoreInst</code></a> from the <code class="language-plaintext highlighter-rouge">users</code> (line <em>220</em>). In line <em>222</em> it goes backwards towards the source (the create function call) and in checks if the function call is <code class="language-plaintext highlighter-rouge">kmem_cache_create</code> (line <em>223</em>). If true the code then extracts the first argument of this function calls which is a hardcoded string containing the name of the cache as shown <a href="https://elixir.bootlin.com/linux/latest/source/include/linux/slab.h#L227">here</a>.</p>

<h3 id="future-work">Future work</h3>

<p>Of course, there is more room for improvement. When dealing with generic allocation calls (ie. <code class="language-plaintext highlighter-rouge">kmalloc</code>) we simply consider the objects to fall into the closest cache that fits it, however this may not always be the case. We can improve this by going backwards from the call graph of these functions to find the <em>kmem_cache</em> from <a href="https://elixir.bootlin.com/linux/latest/source/mm/slab.h#L406"><code class="language-plaintext highlighter-rouge">kmalloc_slab</code></a> function.</p>]]></content><author><name>Erin Avllazagaj</name></author><category term="exploitation" /><category term="linux kernel" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">SyzGPT: When the fuzzer meets the LLM</title><link href="https://albocoder.github.io/fuzzing/exploitation/linux%20kernel/hacking/ai/gpt/llm/2023/11/27/GPT-syzkaller.html" rel="alternate" type="text/html" title="SyzGPT: When the fuzzer meets the LLM" /><published>2023-11-27T00:00:00+00:00</published><updated>2023-11-27T00:00:00+00:00</updated><id>https://albocoder.github.io/fuzzing/exploitation/linux%20kernel/hacking/ai/gpt/llm/2023/11/27/GPT-syzkaller</id><content type="html" xml:base="https://albocoder.github.io/fuzzing/exploitation/linux%20kernel/hacking/ai/gpt/llm/2023/11/27/GPT-syzkaller.html"><![CDATA[<p><a href="/assets/blog4_gpt_fuzzer/gpt_memory_hacker.jpeg"><img src="/assets/blog4_gpt_fuzzer/gpt_memory_hacker.jpeg" alt="Hacker AI cover photo" /></a></p>

<p>In this blog post I publish the results of a project I did during Thanksgiving.
While eating an unhealthy amount of turkey <img src="https://cdn.betterttv.net/emote/63806d6cb9076d0aaebcf83f/1x" alt="" /> I brought up the topic of AI and warned my parents about the risks of its misuse.
One such risk being the biggest nightmare of every security practitioner – AI lowering the bar for petty skids to hack real
systems after they got bored taking L’s in Fortnite.</p>

<p>Security is not a solved problem and I don’t see it being one in the near future (more code = more jobs).
As security researchers, our job is therefore to increase the bar, such that an adversary with a low enough budget doesn’t do anything.
As I was making the case, at some point in the conversation I thought:
<em>What if I can use a large language model (LLM) to help me with fuzzing a linux kernel module that I have no idea of</em>?</p>

<h1 id="background">Background</h1>

<p>Fuzzing the Linux kernel is a widely studied topic. There are various fuzzing tools, such as <a href="https://github.com/nccgroup/TriforceAFL">Triforce AFL</a>, <a href="https://www.ndss-symposium.org/wp-content/uploads/2020/02/24018-paper.pdf">HFL</a>, <a href="https://github.com/google/syzkaller">Syzkaller</a>, etc, the latter being the defacto state-of-the-art open-source kernel fuzzer.
Syzkaller relies on kernel experts’ <strong>manually-written</strong> Syzlang descriptions to generate valid system calls. A description is a file written in syzkaller’s language (Syzlang) that describes how to correctly invoke a systemcall, using the correct parameters, structures and dependencies. For example a <code class="language-plaintext highlighter-rouge">write</code> into a file requires one to <code class="language-plaintext highlighter-rouge">open</code> the file and use it’s return value as a parameter for the earlier.</p>

<p>In <a href="#fig1">Fig. 1</a> I’m showing an example of a Syzlang description (called description from now on) for the <code class="language-plaintext highlighter-rouge">aio</code> module.
The description contains the following:</p>

<ol>
  <li>The required includes to write a valid C program. (yellow highlight)</li>
  <li>The resources that need to be opened, such as file descriptors. (green highlight)</li>
  <li><code class="language-plaintext highlighter-rouge">struct</code> definitions that allow the fuzzer to accurately fuzz complex structures (yellow box)</li>
  <li><code class="language-plaintext highlighter-rouge">const</code>, <code class="language-plaintext highlighter-rouge">enum</code> defitions that restrict the fuzzer to values that make sense (pink bracket)</li>
  <li>The system call defitions that allows the fuzzer to know what parameters to use at which positions and what data type they are. As shown in the figure, some parameters can be resources too. The fuzzer is made aware that it needs to initialize the resource using <code class="language-plaintext highlighter-rouge">io_setup</code> (blue highlight) by passing its integer pointer as an argument.</li>
</ol>

<figure class="image" id="fig1">
    <a href="/assets/blog4_gpt_fuzzer/syzkaller_description_example.png"><img src="/assets/blog4_gpt_fuzzer/syzkaller_description_example.png" alt="Description of `aio` module in syzlang." /></a>
    <figcaption style="text-align: center;"><small>Figure 1: Description of `aio` module in syzlang.</small></figcaption>
</figure>

<h1 id="experimental-setup">Experimental setup</h1>

<p>As mentioned earlier, these descriptions are written manually by kernel developers.
There have been efforts to automate the generation or even reduce these descriptions [<a href="https://www.cs.ucr.edu/%7Ezhiyunq/pub/oakland23_syzdescribe.pdf">1</a>,<a href="https://dl.acm.org/doi/10.1145/3133956.3134069">2</a>,<a href="https://www.usenix.org/conference/atc22/presentation/sun">3</a>,<a href="https://www.ndss-symposium.org/wp-content/uploads/2023/02/ndss2023_f688_paper.pdf">4</a>, <a href="https://dl.acm.org/doi/10.1145/3133956.3134103">5</a>], which is beyond the scope of this blog post (but an interesting followup nonetheless). 
Regardless they all come with some built-in assumptions, and wouldn’t generalize well for a random kernel subsystem. My assumption instead is that a kernel subsystem has a well-written documentation that instructs other non-kernel developers how to use it’s exposed system calls to interact with it.
With this assumption in mind I hypothesize that a Large Language Model (referred as LLM from now on) can read it and understand how to translate that human-readable documentation (ie. a massive <code class="language-plaintext highlighter-rouge">HTML</code> page) into a description for Syzkaller.
To test this assumption I picked a fairly complex subsystem, the <code class="language-plaintext highlighter-rouge">KVM</code>. This subsystem has a rich description in Syzlang, clocking more than <code class="language-plaintext highlighter-rouge">2000</code> lines.
<!--  For a more complete analysis one may need to test this assumption in other subsystems and see if there is a correlation between number of defined struct, number of flags or number of lines of code to the coverage. --></p>

<p>In summary, the experiment is as follows. First, I chose to enable fuzzing on the first 40 system calls from <a href="https://github.com/google/syzkaller/blob/master/sys/linux/dev_kvm.txt"><code class="language-plaintext highlighter-rouge">dev_kvm.txt</code></a>, such as <code class="language-plaintext highlighter-rouge">openat$kvm</code>, <code class="language-plaintext highlighter-rouge">ioctl$KVM_CREATE_VM</code>, etc.
Second, I ran the fuzzer for <strong>9</strong> hours using the original manually written descriptions.
Third, I found the documentation of KVM module from <a href="https://www.kernel.org/doc/html/latest/virt/kvm/api.html">this article</a> and fed it to Bing AI.
Fourth, I removed everything from <code class="language-plaintext highlighter-rouge">dev_kvm.txt</code> and populated it with what the AI wrote to me.
At last, I ran the fuzzer again for <strong>9</strong> hours using the AI-written descriptions.</p>

<h1 id="a-chat-with-the-quickest-reader">A chat with the quickest reader</h1>

<p>In fourth step, I chat back and forth with Bing AI. First I primed it by making sure that it reads the web page (that contains the <code class="language-plaintext highlighter-rouge">KVM</code> documentation) and answers using that as the reference.
I believe Bing AI is using a form of information retrieval system on top of the LLM as in here <a href="https://arxiv.org/pdf/2308.07107.pdf">[5]</a>, which makes it such that the queries are always referred to text from the documentation page.
I opened the tab on the side using the Microsoft edge’s <a href="https://www.microsoft.com/en-us/edge/learning-center/how-to-use-bing-in-sidebar?form=MA13I2">Bing button</a> ans started asking the qeustion.
In the figure below (<a href="#fig2">Fig. 2</a>), I am showing the first message I sent to Bing AI using <em>GPT-4</em>. I asked it to tell me what it reads and see if it can interpret the web page. As it can be seen, the AI was fully capable of reading the article and summarize the syscalls in <strong>4</strong> classes of system calls. This is not a simple rip-off of the original summary, because the part underlined in red is not there in the general description section of the documentation. This means that the GPT was able to read though each of the individual system calls and conclude that <code class="language-plaintext highlighter-rouge">check extension</code> is a feature of the system <em>ioctl</em>s. As also shown in the figure there are plenty of other cases where the GPT is able to provide more concrete examples of each class of syscalls. In the <em>device ioctls</em> however it fails to mention that they can only be called by the process that created the VM, but it’s not a big deal in my opinion.</p>

<figure class="image" id="fig2">
    <a href="/assets/blog4_gpt_fuzzer/chat_start.png"><img src="/assets/blog4_gpt_fuzzer/chat_start.png" alt="Priming the LLM" /></a>
    <figcaption style="text-align: center;"><small>Figure 2: Priming the LLM</small></figcaption>
</figure>

<p>Next, I asked the GPT to generate a program that uses <code class="language-plaintext highlighter-rouge">KVM_CREATE_VM</code> and it wrote it in C. To save some space, I am not posting the interactions that were needed to ask it to write the <code class="language-plaintext highlighter-rouge">C</code> program. I then tested it to make sure it understood how <code class="language-plaintext highlighter-rouge">Syzlang</code> works and whether it can translate the <code class="language-plaintext highlighter-rouge">KVM_CREATE_VM</code> syscall back into <code class="language-plaintext highlighter-rouge">Syzlang</code>. The response is shown in <a href="#fig3">Fig. 3</a>. As you can see, the model is capable of understanding how <code class="language-plaintext highlighter-rouge">Syzlang</code> works and what information from a C program are required by it. Additionally it not only wrote the syscall I asked it, but also the syscall <code class="language-plaintext highlighter-rouge">ioctl$KVM_CREATE_VM</code> is dependent on, <code class="language-plaintext highlighter-rouge">openat$KVM</code>.</p>

<figure class="image" id="fig3">
    <a href="/assets/blog4_gpt_fuzzer/chat_testing.png"><img src="/assets/blog4_gpt_fuzzer/chat_testing.png" alt="Testing LLM's capability to convert C to Syzlang" /></a>
    <figcaption style="text-align: center;"><small>Figure 3: Testing LLM's capability to convert C to Syzlang</small></figcaption>
</figure>

<h1 id="we-need-to-go-deeper">We need to go deeper…</h1>

<p>As you can see above the <code class="language-plaintext highlighter-rouge">ioctl$KVM_CREATE_VM</code> syscall is correct, but it’s a specific one, so it cannot be used to fuzz, because none of the parameters can be modified by the fuzzer. So naturally, I asked GPT to make a generic version of this system call. 
In <a href="#fig4">Fig. 4</a> you can see the output of GPT. It’s asstounding how it not only wrote a generic <code class="language-plaintext highlighter-rouge">Syzlang</code> representation but also explained every parameter, the dependencies and all the possible flag values and the header where they are defined. I was shocked at how well it described the system call and how deep was it’s understanding.
This were my emotions when I read this output <img src="https://cdn.betterttv.net/emote/5f664c989d79b403d93387cc/1x.webp" alt="" /> <img src="https://cdn.betterttv.net/emote/58f6e05e58f5dd226a16166e/1x.webp" alt="" />.</p>

<figure class="image" id="fig4">
    <a href="/assets/blog4_gpt_fuzzer/1_syscall_try.png"><img src="/assets/blog4_gpt_fuzzer/1_syscall_try.png" alt="Writing the Syzlang description for &lt;i&gt;openat$KVM_CREATE_VM&lt;/i&gt;" /></a>
    <figcaption style="text-align: center;"><small>Figure 4: Writing the Syzlang description for <i>openat$KVM_CREATE_VM</i></small></figcaption>
</figure>

<p>Well, now the genie is out of the bottle. Let’s just ask our overlord for more blessings. In <a href="#fig5">Fig. 5</a> you can see our conversation. For the sake of brevity, I won’t be posting the entire result, only the beginning of it. The GPT hit the word limit and it didn’t generate all the syscall descriptions.</p>

<figure class="image" id="fig5">
    <a href="/assets/blog4_gpt_fuzzer/many_syscalls.png"><img src="/assets/blog4_gpt_fuzzer/many_syscalls.png" alt="Making GPT generate syzlang representation of more syscalls" /></a>
    <figcaption style="text-align: center;"><small>Figure 5: Making GPT generate syzlang representation of more syscalls</small></figcaption>
</figure>

<p>As I showed earlier there are 4 things in a description file: (1) the includes, (2) the resources, (3) the syscalls and (4) the struct and flag definitions. We have tackled the first 3. Now let’s ask GPT to tackle the 4th too and bundle up all into 1 so that we can copy and paste.
In the same conversation I asked GPT:</p>

<div id="enabled_syscalls"></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>write me a syzkaller description with all the kvm syscalls mentioned below:


openat$kvm
openat$sgx_provision
ioctl$KVM_CREATE_VM
ioctl$KVM_GET_MSR_INDEX_LIST
ioctl$KVM_CHECK_EXTENSION
ioctl$KVM_GET_VCPU_MMAP_SIZE
ioctl$KVM_GET_SUPPORTED_CPUID
ioctl$KVM_GET_EMULATED_CPUID
ioctl$KVM_X86_GET_MCE_CAP_SUPPORTED
ioctl$KVM_GET_API_VERSION
ioctl$KVM_CREATE_VCPU
ioctl$KVM_CHECK_EXTENSION_VM
ioctl$KVM_GET_DIRTY_LOG
ioctl$KVM_CREATE_IRQCHIP
ioctl$KVM_IRQ_LINE
ioctl$KVM_IRQ_LINE_STATUS
ioctl$KVM_GET_IRQCHIP
ioctl$KVM_SET_IRQCHIP
ioctl$KVM_XEN_HVM_CONFIG
ioctl$KVM_GET_CLOCK
ioctl$KVM_SET_CLOCK
ioctl$KVM_SET_USER_MEMORY_REGION
ioctl$KVM_SET_TSS_ADDR
ioctl$KVM_SET_IDENTITY_MAP_ADDR
ioctl$KVM_SET_BOOT_CPU_ID
ioctl$KVM_PPC_GET_PVINFO
ioctl$KVM_ASSIGN_PCI_DEVICE
ioctl$KVM_DEASSIGN_PCI_DEVICE
ioctl$KVM_ASSIGN_DEV_IRQ
ioctl$KVM_DEASSIGN_DEV_IRQ
ioctl$KVM_SET_GSI_ROUTING
ioctl$KVM_ASSIGN_SET_MSIX_NR
ioctl$KVM_ASSIGN_SET_MSIX_ENTRY
ioctl$KVM_IOEVENTFD
ioctl$KVM_ASSIGN_SET_INTX_MASK
ioctl$KVM_SIGNAL_MSI
ioctl$KVM_CREATE_PIT2
ioctl$KVM_GET_PIT
ioctl$KVM_SET_PIT
ioctl$KVM_GET_PIT2
ioctl$KVM_SET_PIT2
ioctl$KVM_PPC_GET_SMMU_INFO
ioctl$KVM_IRQFD
ioctl$KVM_PPC_ALLOCATE_HTAB
ioctl$KVM_CREATE_DEVICE
ioctl$KVM_REGISTER_COALESCED_MMIO
ioctl$KVM_UNREGISTER_COALESCED_MMIO
ioctl$KVM_SET_NR_MMU_PAGES
ioctl$KVM_GET_NR_MMU_PAGES
ioctl$KVM_REINJECT_CONTROL
ioctl$KVM_HYPERV_EVENTFD
ioctl$KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2
ioctl$KVM_CAP_HALT_POLL
ioctl$KVM_CAP_DIRTY_LOG_RING
</code></pre></div></div>

<p>With this prompt GPT replied as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include &lt;linux/kvm.h&gt;
include &lt;linux/kvm_para.h&gt;
include &lt;linux/kvm_types.h&gt;

resource fd_kvm[fd]
resource fd_kvm_vm[fd]
resource fd_kvm_vcpu[fd]
resource fd_kvm_device[fd]

openat$kvm(fd const[AT_FDCWD], file ptr[in, string["/dev/kvm"]], flags flags[open_flags], mode const[0]) fd_kvm
openat$sgx_provision(fd const[AT_FDCWD], file ptr[in, string["/dev/sgx/provision"]], flags flags[open_flags], mode const[0]) fd_kvm

ioctl$KVM_CREATE_VM(fd fd_kvm, cmd const[KVM_CREATE_VM], arg const[0]) fd_kvm_vm
ioctl$KVM_GET_MSR_INDEX_LIST(fd fd_kvm, cmd const[KVM_GET_MSR_INDEX_LIST], arg ptr[out, kvm_msr_list])
ioctl$KVM_CHECK_EXTENSION(fd fd_kvm, cmd const[KVM_CHECK_EXTENSION], arg int32[kvm_extensions])
ioctl$KVM_GET_VCPU_MMAP_SIZE(fd fd_kvm, cmd const[KVM_GET_VCPU_MMAP_SIZE], arg const[0])
ioctl$KVM_GET_SUPPORTED_CPUID(fd fd_kvm, cmd const[KVM_GET_SUPPORTED_CPUID], arg ptr[inout, kvm_cpuid2])
ioctl$KVM_GET_EMULATED_CPUID(fd fd_kvm, cmd const[KVM_GET_EMULATED_CPUID], arg ptr[inout, kvm_cpuid2])
ioctl$KVM_X86_GET_MCE_CAP_SUPPORTED(fd fd_kvm, cmd const[KVM_X86_GET_MCE_CAP_SUPPORTED], arg ptr[out, kvm_x86_mce])
ioctl$KVM_GET_API_VERSION(fd fd_kvm, cmd const[KVM_GET_API_VERSION], arg const[0])

ioctl$KVM_CREATE_VCPU(fd fd_kvm_vm, cmd const[KVM_CREATE_VCPU], arg int32) fd_kvm_vcpu
ioctl$KVM_CHECK_EXTENSION_VM(fd fd_kvm_vm, cmd const[KVM_CHECK_EXTENSION], arg int32[kvm_extensions])
ioctl$KVM_GET_DIRTY_LOG(fd fd_kvm_vm, cmd const[KVM_GET_DIRTY_LOG], arg ptr[in, kvm_dirty_log])
ioctl$KVM_CREATE_IRQCHIP(fd fd_kvm_vm, cmd const[KVM_CREATE_IRQCHIP], arg const[0])
ioctl$KVM_IRQ_LINE(fd fd_kvm_vm, cmd const[KVM_IRQ_LINE], arg ptr[in, kvm_irq_level])
ioctl$KVM_IRQ_LINE_STATUS(fd fd_kvm_vm, cmd const[KVM_IRQ_LINE_STATUS], arg ptr[inout, kvm_irq_level])
ioctl$KVM_GET_IRQCHIP(fd fd_kvm_vm, cmd const[KVM_GET_IRQCHIP], arg ptr[inout, kvm_irqchip])
ioctl$KVM_SET_IRQCHIP(fd fd_kvm_vm, cmd const[KVM_SET_IRQCHIP], arg ptr[in, kvm_irqchip])
ioctl$KVM_XEN_HVM_CONFIG(fd fd_kvm_vm, cmd const[KVM_XEN_HVM_CONFIG], arg ptr[in, kvm_xen_hvm_config])
ioctl$KVM_GET_CLOCK(fd fd_kvm_vm, cmd const[KVM_GET_CLOCK], arg ptr[out, kvm_clock_data])
ioctl$KVM_SET_CLOCK(fd fd_kvm_vm, cmd const[KVM_SET_CLOCK], arg ptr[in, kvm_clock_data])
ioctl$KVM_SET_USER_MEMORY_REGION(fd fd_kvm_vm, cmd const[KVM_SET_USER_MEMORY_REGION], arg ptr[in, kvm_userspace_memory_region])
ioctl$KVM_SET_TSS_ADDR(fd fd_kvm_vm, cmd const[KVM_SET_TSS_ADDR], arg int64)
ioctl$KVM_SET_IDENTITY_MAP_ADDR(fd fd_kvm_vm, cmd const[KVM_SET_IDENTITY_MAP_ADDR], arg int64)
ioctl$KVM_SET_BOOT_CPU_ID(fd fd_kvm_vm, cmd const[KVM_SET_BOOT_CPU_ID], arg int32)
ioctl$KVM_PPC_GET_PVINFO(fd fd_kvm_vm, cmd const[KVM_PPC_GET_PVINFO], arg ptr[out, kvm_ppc_pvinfo])
ioctl$KVM_ASSIGN_PCI_DEVICE(fd fd_kvm_vm, cmd const[KVM_ASSIGN_PCI_DEVICE], arg ptr[in, kvm_assigned_pci_dev])
ioctl$KVM_DEASSIGN_PCI_DEVICE(fd fd_kvm_vm, cmd const[KVM_DEASSIGN_PCI_DEVICE], arg ptr[in, kvm_assigned_pci_dev])
ioctl$KVM_ASSIGN_DEV_IRQ(fd fd_kvm_vm, cmd const[KVM_ASSIGN_DEV_IRQ], arg ptr[in, kvm_assigned_irq])
ioctl$KVM_DEASSIGN_DEV_IRQ(fd fd_kvm_vm, cmd const[KVM_DEASSIGN_DEV_IRQ], arg ptr[in, kvm_assigned_irq])
ioctl$KVM_SET_GSI_ROUTING(fd fd_kvm_vm, cmd const[KVM_SET_GSI_ROUTING], arg ptr[in, kvm_irq_routing])
ioctl$KVM_ASSIGN_SET_MSIX_NR(fd fd_kvm_vm, cmd const[KVM_ASSIGN_SET_MSIX_NR], arg ptr[in, kvm_assigned_msix_nr])
ioctl$KVM_ASSIGN_SET_MSIX_ENTRY(fd fd_kvm_vm, cmd const[KVM_ASSIGN_SET_MSIX_ENTRY], arg ptr[in, kvm_assigned_msix_entry])
ioctl$KVM_IOEVENTFD(fd fd_kvm_vm, cmd const[KVM_IOEVENTFD], arg ptr[in, kvm_ioeventfd])
ioctl$KVM_ASSIGN_SET_INTX_MASK(fd fd_kvm_vm, cmd const[KVM_ASSIGN_SET_INTX_MASK], arg ptr[in, kvm_assigned_pci_dev])
ioctl$KVM_SIGNAL_MSI(fd fd_kvm_vm, cmd const[KVM_SIGNAL_MSI], arg ptr[in, kvm_msi])
ioctl$KVM_CREATE_PIT2(fd fd_kvm_vm, cmd const[KVM_CREATE_PIT2], arg ptr[in, kvm_pit_config])
ioctl$KVM_GET_PIT(fd fd_kvm_vm, cmd const[KVM_GET_PIT], arg ptr[out, kvm_pit_state])
ioctl$KVM_SET_PIT(fd fd_kvm_vm, cmd const[KVM_SET_PIT], arg ptr[in, kvm_pit_state])
ioctl$KVM_GET_PIT2(fd fd_kvm_vm, cmd const[KVM_GET_PIT2], arg ptr[out, kvm_pit_state2])
ioctl$KVM_SET_PIT2(fd fd_kvm_vm, cmd const[KVM_SET_PIT2], arg ptr[in, kvm_pit_state2])
ioctl$KVM_PPC_GET_SMMU_INFO(fd fd_kvm_vm, cmd const[KVM_PPC_GET_SMMU_INFO], arg ptr[out, kvm_ppc_smmu_info])
ioctl$KVM_IRQFD(fd fd_kvm_vm, cmd const[KVM_IRQFD], arg ptr[in, kvm_irqfd])
ioctl$KVM_PPC_ALLOCATE_HTAB(fd fd_kvm_vm, cmd const[KVM_PPC_ALLOCATE_HTAB], arg int32)
ioctl$KVM_CREATE_DEVICE(fd fd_kvm_vm, cmd const[KVM_CREATE_DEVICE], arg ptr[inout, kvm_create_device]) fd_kvm_device
ioctl$KVM_REGISTER_COALESCED_MMIO(fd fd_kvm_vm, cmd const[KVM_REGISTER_COALESCED_MMIO], arg ptr[in, kvm_coalesced_mmio_zone])
ioctl$KVM_UNREGISTER_COALESCED_MMIO(fd fd_kvm_vm, cmd const[KVM_UNREGISTER_COALESCED_MMIO], arg ptr[in, kvm_coalesced_mmio_zone])
ioctl$KVM_SET_NR_MMU_PAGES(fd fd_kvm_vm, cmd const[KVM_SET_NR_MMU_PAGES], arg int32)
ioctl$KVM_GET_NR_MMU_PAGES(fd fd_kvm_vm, cmd const[KVM_GET_NR_MMU_PAGES], arg const[0])
ioctl$KVM_REINJECT_CONTROL(fd fd_kvm_vm, cmd const[KVM_REINJECT_CONTROL], arg int32)
ioctl$KVM_HYPERV_EVENTFD(fd fd_kvm_vm, cmd const[KVM_HYPERV_EVENTFD], arg ptr[in, kvm_hyperv_eventfd])
ioctl$KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2(fd fd_kvm_vm, cmd const[KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2], arg int32)
ioctl$KVM_CAP_HALT_POLL(fd fd_kvm_vm, cmd const[KVM_CAP_HALT_POLL], arg int32)
ioctl$KVM_CAP_DIRTY_LOG_RING(fd fd_kvm_vm, cmd const[KVM_CAP_DIRTY_LOG_RING], arg int32)

kvm_extensions = KVM_CAP_IRQCHIP, KVM_CAP_HLT, KVM_CAP_MMU_SHADOW_CACHE_CONTROL, KVM_CAP_USER_MEMORY, KVM_CAP_SET_TSS_ADDR, KVM_CAP_VAPIC, KVM_CAP_EXT_CPUID, KVM_CAP_CLOCKSOURCE, KVM_CAP_NR_VCPUS, KVM_CAP_NR_MEMSLOTS, KVM_CAP_PIT, KVM_CAP_NOP_IO_DELAY, KVM_CAP_PV_MMU, KVM_CAP_MP_STATE, KVM_CAP_COALESCED_MMIO, KVM_CAP_SYNC_MMU, KVM_CAP_IOMMU, KVM_CAP_DESTROY_MEMORY_REGION_WORKS, KVM_CAP_USER_NMI, KVM_CAP_SET_GUEST_DEBUG, KVM_CAP_REINJECT_CONTROL, KVM_CAP_IRQ_ROUTING, KVM_CAP_IRQ_INJECT_STATUS, KVM_CAP_ASSIGN_DEV_IRQ, KVM_CAP_JOIN_MEMORY_REGIONS_WORKS, KVM_CAP_MCE, KVM_CAP_IRQFD, KVM_CAP_PIT2, KVM_CAP_SET_BOOT_CPU_ID, KVM_CAP_PIT_STATE2, KVM_CAP
</code></pre></div></div>

<p>As you can see the the definition of the structs is still missing. GPT ran out of words before finishing typing the whole file.
So I asked it to <code class="language-plaintext highlighter-rouge">write the struct definitions such that the system calls you wrote above can be compiled</code>. And GPT replied the <code class="language-plaintext highlighter-rouge">struct</code> definitions.
I noticed that these structs were explicity in the KVM documentation but GPT was smart enough to know how to convert kernel-defined data types to the ones defined by Syzkaller or the userland include clauses. At the end of the post I will link the full GPT-generated description</p>

<h1 id="man-against-machine">Man against machine</h1>

<p>I pulled syzkaller commit <a href="https://github.com/google/syzkaller/commit/8321139737ed27c">8321139737ed27c</a> in 2 different folders. In one I compiled it and in the other I replaced <code class="language-plaintext highlighter-rouge">/sys/linux/dev_kvm.txt</code> with the one generated via Bing AI. Then I let both run on the system calls I mentioned <a href="#enabled_syscalls">here</a>. I let both run for around <strong>9</strong> hours, on 1 VM using <code class="language-plaintext highlighter-rouge">--debug</code> flag of <code class="language-plaintext highlighter-rouge">syz-manager</code>.</p>

<figure class="image" id="fig6">
    <a href="/assets/blog4_gpt_fuzzer/manual_vs_gpt.png"><img src="/assets/blog4_gpt_fuzzer/manual_vs_gpt.png" alt="Fuzzer stats of manually generated(left) VS GPT-generated(right) descriptions." /></a>
    <figcaption style="text-align: center;"><small>Figure 6: Fuzzer stats of manually generated(left) VS GPT-generated(right) descriptions.</small></figcaption>
</figure>

<p>In the <a href="#fig6">Fig. 6</a> I’m reporting the results of running both fuzzers. As you cna see in the left, the manually-written system calls showed a coverage of <code class="language-plaintext highlighter-rouge">6536</code> code blocks when it ran for <code class="language-plaintext highlighter-rouge">9h50min</code> while the GPT-generated one showed a coverage of <code class="language-plaintext highlighter-rouge">5659</code> when it ran for <code class="language-plaintext highlighter-rouge">9h17min</code>. The runtime is not exactly the same but I saw the fuzzers started to plateau. Additionally, the manually generated descriptions showed a max signal of <code class="language-plaintext highlighter-rouge">16813</code> while the GPT-generated one showed only <code class="language-plaintext highlighter-rouge">14110</code>. Either way it took me less than 30 min with GPT to generate <code class="language-plaintext highlighter-rouge">dev_kvm.txt</code>. Did mankind win this <img src="https://cdn.betterttv.net/emote/5aa16eb65d4a424654d7e3e5/1x.webp" alt="" /> ? I will leave it up to your judgement.</p>

<h1 id="conclusion">Conclusion</h1>

<p>To sum up, in this blog post I briefly evaluate the ability of a LLM to automatically understand a kernel subsystem’s documentation to generate <em>Syzlang</em> descriptions, to guide the kernel fuzzer. The results show that for a subset of syscalls of <code class="language-plaintext highlighter-rouge">KVM</code> the manually-written rules outperform the kernel fuzzer. This result is not all-encompassing - other kernel subsystems may show different results. Additionally, this method assumes that a well structured and detailed documnetation on how to call the API is present. This might not always be the case. Nonetheless, GPT demonstrated a significant understanding of the <code class="language-plaintext highlighter-rouge">KVM</code> subsystem and <em>Syzlang</em> and I it not only shortened the time to write a description, but also made it more accessible for a kernel developer with limited knowledge of Syzlang to write one for their own kernel module. To support open science here is the <a href="/assets/blog4_gpt_fuzzer/dev_kvm.txt"><code class="language-plaintext highlighter-rouge">dev_kvm.txt</code></a> file that that the GPT helped generate.</p>]]></content><author><name>Erin Avllazagaj</name></author><category term="fuzzing" /><category term="exploitation" /><category term="linux kernel" /><category term="hacking" /><category term="ai" /><category term="gpt" /><category term="LLM" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">CVE-2022-27666: My file your memory</title><link href="https://albocoder.github.io/exploit/2023/03/13/KernelFileExploit.html" rel="alternate" type="text/html" title="CVE-2022-27666: My file your memory" /><published>2023-03-13T00:00:00+00:00</published><updated>2023-03-13T00:00:00+00:00</updated><id>https://albocoder.github.io/exploit/2023/03/13/KernelFileExploit</id><content type="html" xml:base="https://albocoder.github.io/exploit/2023/03/13/KernelFileExploit.html"><![CDATA[<p><img src="/assets/blog3_cve2022_27666/kernel_exploit_cover_photo.jpg" alt="Blog post cover" /></p>

<h2 id="preface">Preface</h2>

<p>Before I get started with this post, I want to give a shouout to OpenAI’s dall-e for generating this whacky cover photo. I will keep this trend of AI cover photos from now on.</p>

<p>Now lets start by laying the basis of this post. We will be assuming the threat model of an attacker that is running on user-level privileges. Basically, the attacker needs to be able to reach a privileged state that allows them to execute as the <code class="language-plaintext highlighter-rouge">root</code> account. Commonly, exploit authors rely on reaching kernel-context code execution to execute their shellcode which is basically <code class="language-plaintext highlighter-rouge">commit_creds(prepare_kernel_cred(0))</code> as it was explained better by trickster0’s blog <a href="https://trickster0.wordpress.com/2019/04/01/linux-kernel-exploitation-null-pointer-dereference/#:~:text=The%20shellcode%20basically%20needs%20to%20do%20commit_creds%20%28prepare_kernel_cred,assign%20the%200%20privileges%20aka%20root%20privileges.%20">post</a>. Another common method is to overwrite <code class="language-plaintext highlighter-rouge">modprobe_path</code> to point to a file that we can control. When modprobe is executed our file is executed using root privileges.</p>

<p>Some other methods of privilege escalation are not even reliant on kernel bugs, but to misconfiguration of the Operating System. Carlos’ tool, <a href="https://github.com/carlospolop/PEASS-ng">PEASS-ng</a>, is a suite of scripts that are designed to find misconfigured system resources (such as <em>files</em>, <em>directories</em>, <em>sockets</em> etc). After looking at what the scripts are searching for, I noticed that privilege escalation involves:</p>

<ol>
  <li><strong>writing</strong> into root-owned and/or unwritable files or memory</li>
  <li><strong>read</strong> into root-owned and/or unreadable files or memory</li>
</ol>

<p>With that in mind, privilege escalation can be achieved if the attacker can reach any of the 2 states above. We can utilize the linux kernel bugs to reach those states. In fact I demonstrated what kernel structures can be corrupted to reach those states in <a href="/assets/pdf_files/scavy.pdf">SCAVY</a>. I highly encourage the readers to look into that paper.</p>

<h2 id="introduction">Introduction</h2>

<p>In this blog post, I will explore a new approach at exploiting <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-27666"><code class="language-plaintext highlighter-rouge">CVE-2022-27666</code></a>. The explotation technique involves overwriting the <code class="language-plaintext highlighter-rouge">f_mapping</code> pointer in <code class="language-plaintext highlighter-rouge">struct file</code>. As shown in SCAVY, the corruption would allow a read and write into root-owned files. I will explain the technique in the next sections after introducing what the vulnerability is and how we leverage its capability. Basically, the exploit gets write capability to <strong>/etc/passwd</strong> and insert this line: <code class="language-plaintext highlighter-rouge">albocoder:$1$KCPMXNrz$RkFUDj69PHe.T4cGUqzv91:0:0:root:/root:/bin/bash</code>. I will explain more about the payload in the later sections. Our exploit is tested on <strong>Ubuntu 21.10</strong> that runs Linux kernel <strong>5.13.0-19-generic</strong>.</p>

<h2 id="vulnerability">Vulnerability</h2>

<p>As Etenal has described in his excellent <a href="https://etenal.me/archives/1825">blog post</a>, CVE-2022-27666 is a vulnerability in Linux esp6 crypto module where the receiving buffer of a user message is an 8-page buffer, but the sender can send a message larger than 8 pages, which creates a page-wide buffer overflow. For this blog post I will rely on the same starting capability as the prior blog post, an 8-page memory write with the constraint of being the consecutive pages from the vulnerable object. For a more detailed explanation of the vulnerability please read Etenal’s blog.</p>

<h2 id="exploitation">Exploitation</h2>

<p>Our exploitation involves an 8 step procedure. In this section I will briefly summarize our whole attack then we will go into details and reasons why we chose this method in the subsections below.</p>

<p>First, we need to leak the <code class="language-plaintext highlighter-rouge">f_mapping</code> pointer from <code class="language-plaintext highlighter-rouge">struct file</code> (<a href="https://elixir.bootlin.com/linux/v5.13/source/include/linux/fs.h#L955">src</a>). Then overwrite that with the mapping of the struct file of <code class="language-plaintext highlighter-rouge">/etc/passwd</code>. I decided to use the same kernel memory read primitive from the previous blog post to leak the file pointer. Basically, we overflow the <code class="language-plaintext highlighter-rouge">datalen</code> field from <code class="language-plaintext highlighter-rouge">struct user_key_payload</code> to read over the next pages.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct user_key_payload {
	struct rcu_head	rcu;		/* RCU destructor */
	unsigned short	datalen;	/* length of this data */
	char		data[] __aligned(__alignof__(u64)); /* actual data */
};
</code></pre></div></div>

<p>In the pages next to <em>user_key_payload</em> we allocate a large number of <code class="language-plaintext highlighter-rouge">struct vm_area_struct</code> (<a href="https://elixir.bootlin.com/linux/v5.13/source/include/linux/mm_types.h#L304">src</a>) that map from 2 files, a <em>dummy file</em> that is opened with <strong>read/write</strong> permissions and our victim <code class="language-plaintext highlighter-rouge">/ec/passwd</code>. We use the primitive mentioned above to leak <code class="language-plaintext highlighter-rouge">vm_file</code> pointer of the <code class="language-plaintext highlighter-rouge">vm_area_struct</code> that mapped the pages of <em>/etc/passwd</em>. I will get on the details how to find out which <code class="language-plaintext highlighter-rouge">vm_file</code> points to which of the 2 files later.  Next, we use the arbitrary read via <code class="language-plaintext highlighter-rouge">msg_msg</code> to leak the <code class="language-plaintext highlighter-rouge">struct file-&gt;f_mapping</code> pointer for the <code class="language-plaintext highlighter-rouge">struct file</code> of <em>/etc/passwd</em>. And at last we leverage the arbitrary write primitive that relies on <a href="https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html"><code class="language-plaintext highlighter-rouge">msg_msg</code>+<code class="language-plaintext highlighter-rouge">fuse</code> technique</a> to overwrite the <code class="language-plaintext highlighter-rouge">f_mapping</code> pointer of the <code class="language-plaintext highlighter-rouge">struct file</code> of the dummy file.</p>

<p>In summary our exploitation will be as follows:</p>

<ol>
  <li>Massage the heap to account for noise and get the desired layout for the next step.</li>
  <li>Allocate the 8 vulnerable <code class="language-plaintext highlighter-rouge">skb</code> pages followed by the 8 pages of <code class="language-plaintext highlighter-rouge">user_key_payload</code>.</li>
  <li>Allocate some pages of <code class="language-plaintext highlighter-rouge">vm_area_struct</code> next to <code class="language-plaintext highlighter-rouge">user_key_payload</code> in a way that some map a <em>dummy file</em> with <strong>read/write</strong> privileges and some map the <code class="language-plaintext highlighter-rouge">/etc/passwd</code> with <strong>read</strong> privileges.</li>
  <li>Overwrite <code class="language-plaintext highlighter-rouge">datalen</code> from <code class="language-plaintext highlighter-rouge">user_key_payload</code> and read into the page filled with <code class="language-plaintext highlighter-rouge">vm_area_struct</code>’s of the 2 files.</li>
  <li>Leak the <code class="language-plaintext highlighter-rouge">vm_area_struct-&gt;vm_file</code> pointer that points to <code class="language-plaintext highlighter-rouge">struct file</code> of the dummy file and <em>/etc/passwd</em>.</li>
  <li>Use the <code class="language-plaintext highlighter-rouge">msg_msg</code> arbitrary read to read the <code class="language-plaintext highlighter-rouge">file-&gt;f_mapping</code> pointer of the struct file of <em>/etc/passwd</em>.</li>
  <li>Use the <code class="language-plaintext highlighter-rouge">msg_msg</code> arbitrary write primitive to overwrite the <code class="language-plaintext highlighter-rouge">f_mapping</code> pointer of the struct file of our <em>dummy file</em> with the leaked value from step 5.</li>
  <li>Write into <code class="language-plaintext highlighter-rouge">/etc/passwd</code> a line to create a new root-level account</li>
</ol>

<!-- the [msg_msg](https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html) technique. Basically, the simplest idea was to allocate a `struct msg_msg` immediately after the vulnerable object and then a `struct file` right after the msg. That way we can overflow into msg, change the length and read mapping pointer of the file structures. However, quickly we found that there are 2 problems with this approach. By default, (1) ubuntu 21.10 is configured to allow a maximum of only 1000 open files and (2) a large amount of noise in the buddy allocator would never allow 1000 struct file to be allocated next to msg_msg. -->

<h3 id="important-machinations-of-page-allocator">Important machinations of page allocator</h3>

<p>The Linux kernel page allocator is a simple type of buddy allocator with a very basic API. To request a page allocation one can call <code class="language-plaintext highlighter-rouge">__alloc_pages(type, order, nodemask)</code>(<a href="https://elixir.bootlin.com/linux/v5.13/source/mm/page_alloc.c#L5166">src</a>). The parameters are (1) <strong>type</strong> which specifies if the page(s) will be kernel, user or DMA page(s), (2) <strong>order</strong> which specifies the power of 2 for the number of pages to be allocated and (3) <strong>nodemask</strong> which is used to specify which NUMA node to use for allocation. We won’t worry about the NUMA node, since by default the kernel is compiled with a single NUMA node. We also don’t need to worry about the type since the slab allocators all request <em>GFP_KERNEL</em> page types and we can’t mess with that as the attacker. The only thing we care is the <strong>order</strong>, which specifies how many <strong>2<sup>order</sup></strong> pages are we allocating. In the end it returns back a page object pointer that encapsulates all the pages that were allocated. As it’s made clear the buddy allocator can only allocate a certain number of pages from the sequence: <code class="language-plaintext highlighter-rouge">1, 2, 4, 8, 16, ...</code>. Similarly, to free the pages one can call <code class="language-plaintext highlighter-rouge">__free_pages(struct page*, order)</code> (<a href="https://elixir.bootlin.com/linux/v5.13/source/mm/page_alloc.c#L5282">src</a>) which tells how many pages to free from the starting page.</p>

<p>The allocator works in a FIFO manner, meaning that the latest pages to be freed will be returned to satisfy an allocation, but this is not very important to us. What is more important to our exploit is that when freeing an order of pages the allocator will merge consecutive lower order pages if they can make up a higher order one. For example if 2 order 0 pages (meaning 2<sup>0</sup>=1 page) are freed <strong>and</strong> they are consecutive they will be put into the freelist of order <strong>1</strong>. This will come useful when we chose how to mitigate noise on <em>order 3</em> pages. Additionally, to assure that 2 order 3 allocations are consecutive we must exhaust the order 3 freelist and force it to split order 4 pages in 2 order 3’s. I won’t bore you out with more details on the buddy allocator since I already linked the source code of the allocator that our host is using, but if you are interested to know more about its inner machinations check it <a href="https://www.kernel.org/doc/gorman/html/understand/understand009.html">here</a> which is also where I ss’ed figure <a href="#fig1">1</a>.</p>

<figure class="image" id="fig1">
    <a href="/assets/blog3_cve2022_27666/order1_alloc_illustration.png"><img src="/assets/blog3_cve2022_27666/order1_alloc_illustration.png" alt="Freelist illustration of an order 1 page allocation" /></a>
    <figcaption style="text-align: center;"><small>Figure 1: Freelist illustration of an order 1 page allocation</small></figcaption>
</figure>

<p>Additionally, I want to point out that the slab allocator compartmentalizes allocation requests. A call to <code class="language-plaintext highlighter-rouge">kmalloc(size)</code> will round the requested size to the closest power of 2 and find the compartment (offically named <code class="language-plaintext highlighter-rouge">cache</code>) that has objects of that size allocated in its pages. For example, a call to <code class="language-plaintext highlighter-rouge">kmalloc(54)</code> will allocate the object in the <code class="language-plaintext highlighter-rouge">kmalloc-64</code> cache. However, the slab allocator also allows one to request allocations from special caches. For example, a <code class="language-plaintext highlighter-rouge">mmap()</code> call will cause the kernel to call <code class="language-plaintext highlighter-rouge">kmem_cache_alloc("vm_area_struct",sizeof(struct vm_area_struct));</code>, which will allocate a new <code class="language-plaintext highlighter-rouge">vm_area_struct</code> in the special cache with the same name. Similarly, a call to fork() or clone() among other allocations will also allocate a <code class="language-plaintext highlighter-rouge">struct task_struct</code> in the <code class="language-plaintext highlighter-rouge">task_struct</code> cache. To view all the caches and statistics about the number of allocated objects a <em>root</em> user can simply <code class="language-plaintext highlighter-rouge">cat /proc/slabinfo</code>. The output looks like figure <a href="#fig2">2</a>. In the default Ubuntu 20.04 LTS there seem to be 188 caches.</p>

<figure class="image" id="fig2">
    <a href="/assets/blog3_cve2022_27666/slabinfo.png"><img src="/assets/blog3_cve2022_27666/slabinfo.png" alt="Freelist illustration of an order 1 page allocation" /></a>
    <figcaption style="text-align: center;"><small>Figure 2: Freelist illustration of an order 1 page allocation</small></figcaption>
</figure>

<p>The 5 integer columns in the figure above indicate the following:</p>
<ol>
  <li>number of active objects (slots that are allocated)</li>
  <li>total number of object (slots across all pages allocated for this cache)</li>
  <li>object size (in bytes)</li>
  <li>objects per slab (number of slots allocatable in a slab)</li>
  <li>pages per slab (number of pages to allocate whenever a new slab is needed)</li>
</ol>

<p>A slab is a set of 2<sup>order</sup> of pages. Lets illustrate this with an example. Below I added what my <code class="language-plaintext highlighter-rouge">task_struct</code> cache currently looks like. This means that a single <code class="language-plaintext highlighter-rouge">struct</code> is <code class="language-plaintext highlighter-rouge">6400</code> Bytes which is more than 1 page in my system (<code class="language-plaintext highlighter-rouge">PAGE_SIZE = 4096</code>). Therefore, 1 structure takes <strong>~1.56</strong> pages. Currently there is no need to allocate more pages since there are <em>735-639 =</em><strong><em>96</em></strong> free slots in this cache’s freelist, however if all slots were taken the SLUB allocator would call <code class="language-plaintext highlighter-rouge">__alloc_pages(GFP_KERNEL, 3, NULL);</code> and get <em>8 new consecutive pages</em> that have 5 slots to satisfy the allocations.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>task_struct          639    735   6400    5    8 : tunables    0    0    0 : slabdata    147    147      0
</code></pre></div></div>

<h3 id="massaging-the-memory">Massaging the memory</h3>

<p>First we need to make sure that if any cache need pages their allocations don’t mess with our memory. To do that I follow the same logic as Etenal except for the last step that frees all the objects. Therefore the noise mitigation involves 3 steps:</p>

<ol>
  <li>drain the freelist of order 0, 1, 2, 3.</li>
  <li>allocate tons of order-2 objects(assume it’s N), by doing so, order 2 will borrow pages from order 3.</li>
  <li>free every half of objects from step 2, hold the other half. This creates N/2 more object back to order-2’s freelist.</li>
</ol>

<p>This way all objects of order 2 and below will fall in the holes created in step 3. We expect there to be few order 3+ objects so we hope that all our allocations of order 3 pages will be consecutive since after step 1 there are no more order 3 pages scattered around, so the allocator takes 8 pages from order 4. As I illustrate in the GIF below the 4 page hole will satisfy allocations of order 2 to 0. Of course N is in the range of 10-30 so there will be up to 30 holes of 4 pages to accomodate the noise.</p>

<figure class="image" id="fig3">
    <a href="/assets/blog3_cve2022_27666/page_allocator_massaging_animated.gif"><img src="/assets/blog3_cve2022_27666/page_allocator_massaging_animated.gif" alt="Illustration of heap massaging to remove noise" /></a>
    <figcaption style="text-align: center;"><small>Figure 3: Illustration of heap massaging to remove noise</small></figcaption>
</figure>

<h3 id="leaking-pointer-to-struct-file">Leaking pointer to struct file</h3>

<p>Once we have amortized the noise we immediately start with the exploit. As mentioned earlier, I will reuse the same <code class="language-plaintext highlighter-rouge">struct user_key_payload</code> oob read from the previous exploit. I allocate the vulnerable order 3 <code class="language-plaintext highlighter-rouge">skb</code> pages, then allocate 1 <code class="language-plaintext highlighter-rouge">user_key_payload</code> object of size 2049 (1 Byte more than 2048) so that it gets allocated in <code class="language-plaintext highlighter-rouge">kmalloc-4k</code> cache, which is an order 3 slab. Therefore a new slab request from this cache will allocate order 3 pages which will be next to our vulnerable object instead of falling in the noise holes. I didn’t change Etenal’s method of having of 1 <code class="language-plaintext highlighter-rouge">user_key_payload</code> object per <code class="language-plaintext highlighter-rouge">kamlloc-4k</code> slab, because it helps a lot with mitigating problems with order 3 noise. For more info on that to have 9 trials of consecutive <code class="language-plaintext highlighter-rouge">kmalloc-4k</code> slabs, which is because we can allocate up to 9 key payload structures. For more information on why this limit exists check their blog post. Figure <a href="#fig4">4</a> is an illustration of how it would be if we put all <code class="language-plaintext highlighter-rouge">user_key_payload</code> in one slab making the overflow useless if the <code class="language-plaintext highlighter-rouge">user_key_payload</code> didn’t get allocated in the 8 pages next to it. Figure <a href="#fig5">5</a> shows how a more improved noise-resistant overflow looks like, both taken from Etenal’s blog post.</p>

<figure class="image" id="fig4">
    <a href="/assets/blog3_cve2022_27666/slab_layout_noise_prone.png"><img src="/assets/blog3_cve2022_27666/slab_layout_noise_prone.png" alt="Illustration of naive user_key_payload spray" /></a>
    <figcaption style="text-align: center;"><small>Figure 4: Illustration of naive user_key_payload spray</small></figcaption>
</figure>
<figure class="image" id="fig5">
    <a href="/assets/blog3_cve2022_27666/slab_layout_noise_resistant.png"><img src="/assets/blog3_cve2022_27666/slab_layout_noise_resistant.png" alt="Illustration of noise-resistant per-slab allocation" /></a>
    <figcaption style="text-align: center;"><small>Figure 5: Illustration of noise-resistant per-slab allocation</small></figcaption>
</figure>

<p>So far so good, now lets start with our novelty. Once we have the correct slab next to our vulnerable object we need to find an object to leak. I looked into a few candidates that contain a pointer to <code class="language-plaintext highlighter-rouge">struct file</code>. My initial idea was to find a structure in <a href="https://zplin.me/papers/DirtyCred.pdf">dirtycred</a> paper that has a pointer to <code class="language-plaintext highlighter-rouge">struct file</code> and fell in <code class="language-plaintext highlighter-rouge">kmalloc-4k</code> or any order 3 slab. However I found that <code class="language-plaintext highlighter-rouge">vduse_iova_domain</code> correspond to vdev device which is not supported in the stock Ubuntu 21.10 that I’m exploiting. So next I tried to change the exploitation strategy and go for corrupting <code class="language-plaintext highlighter-rouge">struct task_struct</code> instead. As we show in <a href="assets/pdf_files/scavy.pdf">our paper</a> there are multiple fields in <code class="language-plaintext highlighter-rouge">task_struct</code> that we can corrupt, however there were 2 problems: (1) when calling <code class="language-plaintext highlighter-rouge">fork()</code> the kernel would allocate <code class="language-plaintext highlighter-rouge">task_struct</code> along with hundreds of other structures, which would leak out of the noise holes we made earlier and mess with our exploit’s memory layout, (2) we would first need to leak the memory address of <code class="language-plaintext highlighter-rouge">task_struct</code> since our primitive is a read and not a write. Specifically for problem 2 we needed to know where the <code class="language-plaintext highlighter-rouge">task_struct</code> is allocated so that when we use <code class="language-plaintext highlighter-rouge">msg_msg</code>’s arbitrary write we know which address to write to.</p>

<p>Given these 2 problems I decided on a different apporach, which I also introduced in the beginning. I start by first opening 2 files: a dummy file (lets call it <code class="language-plaintext highlighter-rouge">/tmp/dumdum</code>) with read and write permissions and <code class="language-plaintext highlighter-rouge">/etc/passwd</code> with read permission (because we can’t write into it as a non-root user). This causes kernel to allocate 1 <code class="language-plaintext highlighter-rouge">struct file</code> for each <code class="language-plaintext highlighter-rouge">open()</code>. One idea is to just open a bunch of files with the hope that a page of <code class="language-plaintext highlighter-rouge">struct file</code>s gets allocated next to <code class="language-plaintext highlighter-rouge">user_key_payload</code>. However, we have the same problem here as we did with <code class="language-plaintext highlighter-rouge">task_struct</code>, which is that we don’t know the <code class="language-plaintext highlighter-rouge">struct file</code>’s location in memory to overwrite. Therefore, I decided to mmap() the opened files instead, which creates <code class="language-plaintext highlighter-rouge">vm_area_struct</code> structures for each successful mapping. As you can see below the structure has <code class="language-plaintext highlighter-rouge">vm_next</code> and <code class="language-plaintext highlighter-rouge">vm_prev</code> which are used in the context of a doubly-linked list. Since every allocation will link to the previous and next we can find the exact page address. Additionally having <code class="language-plaintext highlighter-rouge">vm_start</code> and <code class="language-plaintext highlighter-rouge">vm_end</code> allows us to better search for the structure since we know these values from the return value of the <code class="language-plaintext highlighter-rouge">mmap()</code> call.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct vm_area_struct {
	unsigned long vm_start;
	unsigned long vm_end;
	struct vm_area_struct *vm_next, *vm_prev;
	struct rb_node vm_rb;
	unsigned long rb_subtree_gap;
	struct mm_struct *vm_mm;
	pgprot_t vm_page_prot;
	unsigned long vm_flags;

	struct {
		struct rb_node rb;
		unsigned long rb_subtree_last;
	} shared;

	struct list_head anon_vma_chain;
	struct anon_vma *anon_vma;
	const struct vm_operations_struct *vm_ops;
	unsigned long vm_pgoff;
	struct file * vm_file;
	void * vm_private_data;

#ifdef CONFIG_SWAP
	atomic_long_t swap_readahead_info;
#endif
#ifndef CONFIG_MMU
	struct vm_region *vm_region;	/* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;
</code></pre></div></div>

<p>So lets summarize our exploit so far. As I illustrate in Figure <a href="#fig6">6</a>, we massage the heap and poke holes, then allocate the vulnerable object and <code class="language-plaintext highlighter-rouge">user_key_payload</code>, opent the 2 files and mmap them. Then we overwrite <code class="language-plaintext highlighter-rouge">user_key_payload-&gt;datalen</code> field and read out of bound into the page(s) containing <code class="language-plaintext highlighter-rouge">vm_area_struct</code>.</p>

<figure class="image" id="fig6">
    <a href="/assets/blog3_cve2022_27666/read_primitive_illustration.gif"><img src="/assets/blog3_cve2022_27666/read_primitive_illustration.gif" alt="Illustration of how to read vm_area_struct" /></a>
    <figcaption style="text-align: center;"><small>Figure 6: Illustration of how to read vm_area_struct</small></figcaption>
</figure>

<p>As we see, we don’t want to poke too many order 2 holes or all the <code class="language-plaintext highlighter-rouge">mmap()</code> calls will be falling into those holes. The way we allocate <code class="language-plaintext highlighter-rouge">vm_area_struct</code> is my <code class="language-plaintext highlighter-rouge">mmap</code>‘ing the dummy file 3 out of 4 times and the passwd 1. This ratio is important because it makes it easy to find which <code class="language-plaintext highlighter-rouge">vm_area_struct-&gt;vm_file</code> points the <code class="language-plaintext highlighter-rouge">struct file</code> of <code class="language-plaintext highlighter-rouge">/etc/passd</code> and which one points to the file of <code class="language-plaintext highlighter-rouge">/tmp/dumdum</code>. So let’s talk more in detail how we are doing this. After I <code class="language-plaintext highlighter-rouge">open</code> the 2 files I <code class="language-plaintext highlighter-rouge">mmap</code> them as shown below.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>void* mapped[3000];
int fd = open("/etc/passwd", O_RDONLY);
int fd_dummy = open("/tmp/dumdum", O_RDWR);
for (int i = 0; i &lt; 3000; i++) {
	if (i % 4 ==0)
		mapped[i] = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, 0);
	else
		mapped[i] = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd_dummy, 0);
}
</code></pre></div></div>

<p>As we can see above, <code class="language-plaintext highlighter-rouge">vm_area_struct</code> has the <code class="language-plaintext highlighter-rouge">__randomize_layout</code> which makes it harder to find the offset of each field. I personally found a hacky way to do it, which a user-level attacker wouldn’t be able to do. So I created a fake kernel module (say <code class="language-plaintext highlighter-rouge">fake.ko</code>), compiled it and then just ran <code class="language-plaintext highlighter-rouge">pahole -C vm_area_struct fake.ko</code> and it would show exactly how the structure is defined in the running kernel. If you want to learn more about this hack <a href="https://stackoverflow.com/questions/58967047/how-can-i-find-out-the-field-offsets-of-a-kernel-struct">here</a> is a stackoverflow post. Since <code class="language-plaintext highlighter-rouge">vm_area_struct</code> has many null fields and 2 known-value fields it’s fairly easy for an attacker to figure out the random layout.</p>

<p>So now we have leaked at least 1 page full of <code class="language-plaintext highlighter-rouge">vm_area_struct</code>. Now we need to find which <code class="language-plaintext highlighter-rouge">f_mapping</code> pointer points to <code class="language-plaintext highlighter-rouge">/tmp/dumdum</code> and which one points to <code class="language-plaintext highlighter-rouge">/etc/passwd</code>. To do that we simply <strong>count</strong> all the occurrences. The one occurring more often is the address of <code class="language-plaintext highlighter-rouge">struct file</code> of our dummy file. Additionally, while we don’t need to we can also count all <code class="language-plaintext highlighter-rouge">vm_next</code> and <code class="language-plaintext highlighter-rouge">vm_prev</code> to find the start of the page that contains our vm structures and from there we can find the address of <strong>every</strong> structure that we leaked.</p>

<h3 id="closing-in-the-exploit-with-pre-written-primitives">Closing in the exploit with pre-written primitives</h3>

<p>After allocating 3000 <code class="language-plaintext highlighter-rouge">vm_area_struct</code>s we no longer have order 2 noise holes, so first we re-massage the heap and use the same <code class="language-plaintext highlighter-rouge">msg_msg</code> arbitrary read technique to read <code class="language-plaintext highlighter-rouge">file-&gt;f_mapping</code> value from the <code class="language-plaintext highlighter-rouge">struct file</code> of <code class="language-plaintext highlighter-rouge">/etc/passwd</code>. As we can see below the file structure has <code class="language-plaintext highlighter-rouge">__randomize_layout</code> too. I again used the same hacky way to get the correct layout, but I believe one can leak both struct files and figure out the pattern given that there are shared values such as <code class="language-plaintext highlighter-rouge">f_op</code> pointer. For more on how this read primitive works check Etenal’s blog post.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct file {
	union {
		struct llist_node	fu_llist;
		struct rcu_head 	fu_rcuhead;
	} f_u;
	struct path		f_path;
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;

	/*
	 * Protects f_ep, f_flags.
	 * Must not be taken from IRQ context.
	 */
	spinlock_t		f_lock;
	enum rw_hint		f_write_hint;
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;
	struct mutex		f_pos_lock;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;

	u64			f_version;
#ifdef CONFIG_SECURITY
	void			*f_security;
#endif
	/* needed for tty driver, and maybe others */
	void			*private_data;

#ifdef CONFIG_EPOLL
	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct hlist_head	*f_ep;
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;
	errseq_t		f_wb_err;
	errseq_t		f_sb_err; /* for syncfs */
} __randomize_layout
  __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */
</code></pre></div></div>

<p>Then, the prior exploit uses a <code class="language-plaintext highlighter-rouge">msg_msg</code> arbitrary write primitive to overwrite <code class="language-plaintext highlighter-rouge">modprobe_path</code>, a <a href="https://lkmidas.github.io/posts/20210223-linux-kernel-pwn-modprobe/">common</a> target for attackers. I modified their <code class="language-plaintext highlighter-rouge">arb_write()</code> code to overwrite the <code class="language-plaintext highlighter-rouge">file-&gt;f_mapping</code> of the <code class="language-plaintext highlighter-rouge">struct file</code> of the dummy file. After the overwrite succeded the <code class="language-plaintext highlighter-rouge">read(fd_dummy,buf,6);</code> returned <code class="language-plaintext highlighter-rouge">root:x</code>, which is the content in <code class="language-plaintext highlighter-rouge">/etc/passwd</code>. <strong>SUCCESS!</strong></p>

<p>All that was left was to write into the file and close it. So I decided to create a root account with the name <em>albocoder</em> and password <em>erin</em>. Of course Linux doesn’t just allow for passwords to be saved in plaintext so I hashed it with the command <code class="language-plaintext highlighter-rouge">openssl passwd -1 erin</code>. This generates a hash that Linux will get to when we supply <em>erin</em> as the password in <code class="language-plaintext highlighter-rouge">su</code>. So first I <code class="language-plaintext highlighter-rouge">fseek</code> to the beginning of the file and then call <code class="language-plaintext highlighter-rouge">write()</code> for <code class="language-plaintext highlighter-rouge">albocoder:$1$KCPMXNrz$RkFUDj69PHe.T4cGUqzv91:0:0:root:/root:/bin/bash</code>. Last, we call exit(0) and boom, Bob’s your uncle…</p>

<p>Now we simply do <code class="language-plaintext highlighter-rouge">su albocoder</code> insert <code class="language-plaintext highlighter-rouge">erin</code> as the password and the <code class="language-plaintext highlighter-rouge">id</code> command returns the uid <code class="language-plaintext highlighter-rouge">0</code>. Since I tried to keep most of the code the same as the previous exploit code I will only publish in the github repository the files that have changed <a href="https://github.com/Albocoder/cve-2022-27666-exploits">here</a>.</p>

<h2 id="video-demo">Video demo</h2>

<iframe width="730" height="400" src="https://www.youtube.com/embed/wvtmWiotlhc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="">
</iframe>

<h2 id="vm_area_struct-exploit">vm_area_struct exploit</h2>
<p>Read our <a href="/assets/pdf_files/scavy.pdf">paper</a>, that was accepted at USENIX 2024.</p>

<h2 id="ack">Ack</h2>
<p>Thanks to Etenal who helped me understand his exploit code and troubleshoot the exploit crashing when trying to corrupt <code class="language-plaintext highlighter-rouge">task_struct</code> by calling <code class="language-plaintext highlighter-rouge">fork()</code> 100 times.</p>]]></content><author><name>Erin Avllazagaj</name></author><category term="exploit" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Analysing malware variability in the real world</title><link href="https://albocoder.github.io/malware/2021/08/10/malware-behavior-variability.html" rel="alternate" type="text/html" title="Analysing malware variability in the real world" /><published>2021-08-10T00:00:00+00:00</published><updated>2021-08-10T00:00:00+00:00</updated><id>https://albocoder.github.io/malware/2021/08/10/malware-behavior-variability</id><content type="html" xml:base="https://albocoder.github.io/malware/2021/08/10/malware-behavior-variability.html"><![CDATA[<p><a href="/assets/blog2_malware_behavior/cover.jpg"><img src="/assets/blog2_malware_behavior/cover.jpg" alt="Malware variability" /></a></p>

<p>This post is a gentle overview of <a href="https://www.usenix.org/system/files/sec21-avllazagaj.pdf">our paper</a>. I will present some other interesting results we didn’t include in the paper
that might interest the broader audience and the industry.</p>

<h1 id="introduction">Introduction</h1>

<p>Dynamic malware analysis is the go-to tool for clustering malware into families<a href="https://mdbailey.ece.illinois.edu/publications/raid07_final.pdf">[1]</a><a href="https://sites.cs.ucsb.edu/~chris/research/doc/ndss09_cluster.pdf">[2]</a><a href="https://www.sec.cs.tu-bs.de/pubs/2008-dimva.pdf">[3]</a><a href="https://dl.acm.org/doi/10.5555/2011216.2011217">[4]</a> or detecting malware in cases where static analysis would fail<a href="https://ieeexplore.ieee.org/document/4413008">[5]</a><a href="http://ids.cs.columbia.edu/sites/default/files/ccs07poly.pdf">[6]</a>, such as polymorphic malware.
Prior work have proposed techniques to detect malware based off the execution traces<a href="https://dl.acm.org/doi/10.1145/2338965.2336768">[7]</a><a href="https://people.eecs.berkeley.edu/~sseshia/pubdir/oakland05.pdf">[8]</a><a href="https://ieeexplore.ieee.org/document/7280815">[9]</a><a href="https://www.usenix.org/legacy/events/sec09/tech/full_papers/kolbitsch.pdf">[10]</a><a href="https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.154.9&amp;rep=rep1&amp;type=pdf">[11]</a>.
However, even with these impressive detection rates, malware outbreaks still happen and <a href="https://www.ndss-symposium.org/wp-content/uploads/2019/02/ndss2019_02B-3_Herwig_paper.pdf">pack a serious punch</a>. So there must be some discrepancy between what analyst see in the labs and how malware behave in the wild.
In <a href="https://www.usenix.org/system/files/sec21-avllazagaj.pdf">our paper</a> we studied the behavior of malware in the wild, for a dataset collected across 5.4M machines across the globe.
In this blog post I will showcase some more results including some case studies.
In our work, we answer the following questions:</p>

<ol>
  <li>How much variability is there in malware behavior in the wild across machines and time?</li>
  <li>What parts of its behavior varies more?</li>
  <li>How does this variability affect existing malware clustering and detection methods?</li>
  <li>Can we still find invariants in a malware’s behavior? What parts of the behavior profile are more likely to yield invariants?</li>
</ol>

<p>For the sake of brevity, in this report I will briefly report our results on questions 1, 3 and 4, while providing some motivating case studies.</p>

<h1 id="dataset">Dataset</h1>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/dataset_executions.png"><img src="/assets/blog2_malware_behavior/dataset_executions.png" alt="Figure 1: Number of execution for various countries" /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 1: Number of execution for various countries</small></figcaption>
</figure>

<p>We are able to answer all the aforementioned questions through a dataset of program executions, we collected from <strong>5.4M</strong> machines across <strong>113</strong> countries.
In Figure 1, we show a distribution of the program executions in our dataset. Evidently, the majority comes from USA, China, Russia and the European countries.</p>

<table>
  <thead>
    <tr>
      <th>OS version</th>
      <th>% execs</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Win 7 build 7601</td>
      <td>56%</td>
    </tr>
    <tr>
      <td>Win 10</td>
      <td>35%</td>
    </tr>
    <tr>
      <td>Win 8.1</td>
      <td>3.1%</td>
    </tr>
    <tr>
      <td>Win Server</td>
      <td>2.6%</td>
    </tr>
    <tr>
      <td>Win XP</td>
      <td>2%</td>
    </tr>
    <tr>
      <td>Other</td>
      <td>1.3%</td>
    </tr>
  </tbody>
</table>

<p>As we see from the table above the most popular windows OS version in <strong>2018</strong> was <em>Win 7 build 7601</em>. This may no longer be the case in 2021 and the threat
landscape may not be the same, but the malware behavior must be more evasive. Therefore the behavior variability in the wild should only be higher.</p>

<h1 id="variability">Variability</h1>

<p>We measure the variability in terms of <em>missing</em> and <em>additional</em> actions in malware, PUP and benign execution traces across <em>machines</em> and <em>time</em>.
In this blog post I will only focus on cross-machine variability.<br />
For more details on the exact definition of variability and other results 
I would encourage you to check section 2 in the <a href="https://www.usenix.org/system/files/sec21-avllazagaj.pdf">paper</a>.</p>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/machine_variability.png"><img src="/assets/blog2_malware_behavior/machine_variability.png" alt="Figure 2: Number of missing/additional actions across machines in malware, PUP and benign samples." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 2: Number of missing/additional actions across machines in malware, PUP and benign samples.</small></figcaption>
</figure>

<p>In the boxplots in Figure 2, we plot the number of missing/addional actions across machines for all malware, PUP and benign samples. As you can see,
at least 50% of the malware samples in our dataset have around <strong>59</strong> missing/additional events in the trace where <strong>33</strong> are file creations (Figure 2b).</p>

<p>To better zoom in on this variability we built a Splunk dashboard with information about difference between 2 traces. Below I will present some of the variability we found in the wild
and the cause of it.</p>

<h4 id="case-study-the-ramnit-worm-vt-link">Case study: <a href="https://www.virustotal.com/gui/file/fd6c69c345f1e32924f0a5bb7393e191b393a78d58e2c6413b03ced7482f2320/detection">The Ramnit worm (VT link)</a></h4>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/ramnit_splunk_dashboard.png"><img src="/assets/blog2_malware_behavior/ramnit_splunk_dashboard.png" alt="Figure 3: SPLUNK dashboard showing the comparison of 2 executions of Ramnit on Windows 7 (left) and Windows 10 (right)." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 3: SPLUNK dashboard showing the comparison of 2 executions of Ramnit on Windows 7 (left) and Windows 10 (right).</small></figcaption>
</figure>

<p>An interesting case was that of Ramnit worm. The analysts from <a href="https://www.cert.pl/en/posts/2017/09/ramnit-in-depth-analysis/">cert.pl blog</a> have confirmed that if the Ramnit worm is
executed with non-admin privilleges it will try to privillege escalation. In the vulnerable version of Windows 7 it will exploit
<a href="https://nvd.nist.gov/vuln/detail/CVE-2013-3660">CVE-2013-3660</a>.
From exploit-db we found a <a href="https://www.exploit-db.com/exploits/25611">confirmed working exploit code</a>, which seems to create mutexes. The large amount of Mutex creations may happen
because the malware keeps running the exploit until it succeeds.</p>

<p>Going back to Figure 3, the green bar plot in the middle is the absolute difference in the number of actions for each event type. As we can see from the blue graph on the left, trace 1 
has around 50 mutex creation actions while the plot on the right doesn’t have any. This result shows what the analysts of 
<a href="https://www.cert.pl/en/posts/2017/09/ramnit-in-depth-analysis/">cert.pl blog</a> have manually seen.</p>

<p>This environment-sensitive behavior, however happens very often<a href="https://publik.tuwien.ac.at/files/PubDat_204766.pdf">[12]</a> in malware thus it’s to be expected that the behavior 
variablity in the wild is the largest among the 3 categories (malware, PUP and benign).</p>

<h4 id="case-study-the-darkcomet-rat-vt-link">Case study: <a href="https://www.virustotal.com/gui/file/4af6714b299b58b1f31ee5d0f529badafbad70ce236dab8faba2d25a0d317e81/detection">The Darkcomet RAT (VT link)</a></h4>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/darkkomet_splunk_dashboard.png"><img src="/assets/blog2_malware_behavior/darkkomet_splunk_dashboard.png" alt="Figure 4: SPLUNK dashboard showing the comparison of 2 executions of Darkcomet on 2 different times and 2 different machines." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 4: SPLUNK dashboard showing the comparison of 2 executions of Darkcomet on 2 different times and 2 different machines.</small></figcaption>
</figure>

<p>Another case of variability across machines or time is the Darkcomet RAT. In particular case, variabilty is hard to be correctly attributed to a root cause, since the RAT may change its
behavior in a specific machine (as instructed by the attacker) or through time if the attacker start an attack campaign (ie. DDoS). Nevertheless, we notice that in trace 2 the malware 
creates Registry keys while in trace 1 it doesn’t, meanwhile in trace 2 it creates a lot more files. This could be due to new commands issued by the C2.</p>

<h1 id="invariant">Invariant</h1>

<p><strong>With all this behavior variability can the analyst still detect the malware?</strong>
We turn our attention to host-based IDS systems (ie. <a href="https://www.splunk.com/">Splunk</a>, <a href="https://www.sumologic.com/">Sumo logic</a>,
<a href="https://www.ibm.com/security/security-intelligence/qradar">Qradar</a> or other SIEM tools). In this section, we measure the effectiveness of most common</p>

<h4 id="methodology">Methodology</h4>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/tokenization.png"><img src="/assets/blog2_malware_behavior/tokenization.png" alt="Figure 5: Tokenization of SIGMA rules' parameters. Octopus Scanner detection rule (left) and OceanLotus Registry activity detection rule (right)." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 5: Tokenization of SIGMA rules' parameters. Octopus Scanner detection rule (left) and OceanLotus Registry activity detection rule (right).</small></figcaption>
</figure>

<p>To answer this question, we first scrapped all the rules from <a href="https://github.com/SigmaHQ/sigma">Sigma</a> to analyze their composition. In Figure 5, we show 2 SIGMA rules, in which we 
split the parameters’ values into tokens via <a href="https://ss64.com/nt/syntax-esc.html">windows delimiters</a>. The tokens are shown in <b style="color: red;">red</b>. To highlight the 
importance of this tokenization we perform it for all the SIGMA rules and we consider a full token to exist if the rule shows <em>3</em> tokens with no regular expresssion character in 
between, this way we eleminate cases where only a part of the token is present, such as the <b style="color: green;">green</b> box.
We noticed that around <strong>70%</strong> of all the existing open source rules have at least 1 full token in the middle, as shown in <strong>black</strong>. Considering our approach, this is a lower bound
estimate on the true value of full token matching rules.</p>

<p>I’m not highlighting all the tokens with all their resepective colors, but you get the point.</p>

<p>This is important because now we know that if 1 of these tokens is doesn’t match the entire rule will not. So we go back to our dataset and split all the parameters of all our 
execution actions into windows delimited tokens. Our goal is to analyze the <em>prevalence</em> of these tokens in the wild.</p>

<p>Basically we are not interested in common tokens (ie. <code class="language-plaintext highlighter-rouge">exe</code>, <code class="language-plaintext highlighter-rouge">windows32</code>, <code class="language-plaintext highlighter-rouge">setup</code>, <code class="language-plaintext highlighter-rouge">cmd</code>), but in malware-specific tokens (ie. <code class="language-plaintext highlighter-rouge">wnry</code>) since the latter will be used in a SIEM signature.
Therefore, the first step from our analysis is to remove all tokens that appear in <em>any</em> of the benign samples and all tokens that appear only in 1 machine (randomness). We assume the 
analyst will use their knowledge or a data-driven method to remove randomness. The leftover from the culling are considered to be malware-specific tokens. We consider the set of all the
tokens left as the invariant, since an analyst can create IDS rules that check the presence of at least 1 of the tokens, in the action’s parameter.</p>

<blockquote>
  <p>The invariant is a bag/set of tokens.</p>
</blockquote>

<h4 id="gotta-catch-em-all">Gotta catch em’ all</h4>

<p>The first measurement we conduct is to evaluate the number of minimum number of machines an analyst would need to capture all malware-specific tokens.
The question we answer is: <code class="language-plaintext highlighter-rouge">How many machines are needed to capture all malware-specific tokens for most common parameters?</code>. We consider common,
the parameters most used by the SIGMA rules. Of course, this assumes that the malware author will execute the malware at the perfect time and system
to obtain the largest amount of tokens. This estimate is of course a lower bound, but it serves to illustrate the difficulty in obtaining the tokens
needed for SIEM signatures.w</p>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/machines_capture_all_tokens.png"><img src="/assets/blog2_malware_behavior/machines_capture_all_tokens.png" alt="Figure 6: Number of machines needed to capture all malware-specific tokens." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 6: Number of machines needed to capture all malware-specific tokens.</small></figcaption>
</figure>

<p>As we see from Figure 6, for different parameters it requires different amount of machines to get all the values. The hardest to capture seems to be the file name where only around 85%
of the malware yield all their malware-specific tokens within 10 machines. In SIGMA, the dropped file’s name makes up for <strong>~12%</strong> of the rules.</p>

<p>On the other hand, the command line is used in <strong>~40%</strong> of the SIGMA rules and rightfully so. Figure 6 confirms that it takes less machines to get the malware-specific tokens.</p>

<h4 id="how-successful-in-detection-are-the-tokens">How successful in detection are the tokens?</h4>

<p>While we show how difficult to catch are some tokes from some of the parameters, we also want to know which tokens are the best to be used in detection.
For this we measure the ratio of machines in the wild each malware-specific tokens appears. We name the ratio of machines the token appears in the wild as <em>coverage</em>.</p>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/token_coverage_rates.png"><img src="/assets/blog2_malware_behavior/token_coverage_rates.png" alt="Figure 7: Coverage of all non-benign tokens (random and malware-specific)." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 7: Coverage of all non-benign tokens (random and malware-specific).</small></figcaption>
</figure>

<p>In Figure 7, we measure the coverage of all non-benign tokens an analyst will find in a malware execution trace. The graph can be interpreted as the success rate (coverage)
an analyst would get if they picked 1 single random token to make their signature of. Evidently, file path tokens (subdirectories) and command line tokens (CMD parameters)
seem to yield better coverage.</p>

<p>But an analyst will not use 1 token and go their merry way. They will pick a all tokens (since by definition they don’t appear in benign samples).
In the following sections we will determine how many machines an analyst needs to get the largest coverage.
In the <a href="https://www.usenix.org/system/files/sec21-avllazagaj.pdf">paper</a>, we measure how often the analyst needs to reexecute the malware to keep the signatures up-to-date.</p>

<h2 id="optimal-number-of-machines">Optimal number of machines</h2>

<p>In this section we measure the success of detection in the wild, for a set of tokens extracted by <code class="language-plaintext highlighter-rouge">N</code> machines. We assume that the malware would behave in the analysts’ sandboxes
as if they are running in the wild. The question we answer is: “<code class="language-plaintext highlighter-rouge">In how many machines should the analyst execute the malware sample to get the largest coverage of the token set?</code>”
We define as coverage, the amount of machines in the wild where 1 of the tokens in the set appears. In a more formal definition, a machine is considered “covered” if the intersection
of the “signature” set with the set of tokens in that machine is not empty.</p>

<figure class="image" id="fig">
    <a href="/assets/blog2_malware_behavior/total_coverage_n_machines.png"><img src="/assets/blog2_malware_behavior/total_coverage_n_machines.png" alt="Figure 8: Total coverage obtained from N machines for 3 most common parameters." /></a>
    <figcaption style="text-align: center;"><small>Figure : Figure 8: Total coverage obtained from N machines for 3 most common parameters.</small></figcaption>
</figure>

<p><strong>But how can an analyst generate N VMs to resemble N random machines in the wild?</strong> - We discuss that an analyst can use a random VM generator like SecGen
<a href="https://www.usenix.org/conference/ase17/workshop-program/presentation/schreuders">[13]</a> with the features proposed in Spottless Sandboxes paper
<a href="https://ieeexplore.ieee.org/document/7958622">[14]</a>. Of course vendors have an idea on the distribution of those features in the wild, so for analysts in such companies generating 
VMS that resemble the true population of users’ machines in the wild is not a big challenge.</p>

<p>From Figure 8, we notice that an analyst needs to execute the malware sample in <code class="language-plaintext highlighter-rouge">3</code> machines to get the best coverage using file name tokens. Adding more machines only gives
diminishing returns. For more results please refer to our <a href="https://www.usenix.org/system/files/sec21-avllazagaj.pdf">paper</a>.</p>

<h1 id="effects-of-behavior-variability-in-malware-clustering">Effects of behavior variability in malware clustering</h1>

<p>As I mentioned earlier, clustering is a very popular method to deal with polymorphic malware samples. Analysts use it to determine if a newly seen executable belongs to a malware
family based off the behavior it shows in the sandbox. 
However, they usually only use <code class="language-plaintext highlighter-rouge">1</code> execution per malware sample to determine the cluster (ie. malware family) the sample belongs to.</p>

<p>In this section we will analyze how effective clustering is in the wild. The closest clustering paper that uses features similar to ours is that by Bailey et al.
<a href="https://mdbailey.ece.illinois.edu/publications/raid07_final.pdf">[15]</a>. Our goal is not to find the malware families, but to argue the validity of clustering results.
The core idea is that <code class="language-plaintext highlighter-rouge">executions from the same sample/hash should fall in the same cluster</code>.
For that we pick 4 random executions per malware sample and perform the clustering.</p>

<table>
  <thead>
    <tr>
      <th>number of clusters</th>
      <th>% of malware samples</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>67%</td>
    </tr>
    <tr>
      <td>2</td>
      <td>27%</td>
    </tr>
    <tr>
      <td>3</td>
      <td>5%</td>
    </tr>
    <tr>
      <td>4</td>
      <td>1%</td>
    </tr>
  </tbody>
</table>

<p>Since we pick 4 executions per sample we count the number of clusters those 4 executions fall in. In the table above we show the malware samples for each the 4 executions fall in 1,2,3
and 4 different clusters. We noticed that <code class="language-plaintext highlighter-rouge">33%</code> of the malware samples have executions in 2 different clusters, therefore if we were to interpret each cluster as a malware family
it’s not clear which family they belong to. Surprisingly, <code class="language-plaintext highlighter-rouge">1%</code> of the malware have executions in 4 different clusters.</p>

<h1 id="conclusion">Conclusion</h1>

<p>It has been known, for over a decade, that malware samples
can change their behavior on different hosts and at different
points in time, but no study has yet measured this variability
in the real world. In this paper, we report the first analysis of
malware, PUP and benign-sample behavior in the wild, using
execution traces collected from <code class="language-plaintext highlighter-rouge">5.4M</code> real hosts from around
the world. We show that malware exhibits more variability
than benign samples.
The causes may be different based on the malware type,
vulnerabilities in the victims’ machines etc.</p>

<p>We then assess the prevalence
of invariant parameter tokens that are commonly used to
derive behavior based signatures for malware.
Our results suggest that analysts should re-execute
the malware samples <code class="language-plaintext highlighter-rouge">3</code> weeks after first receiving them to
update their behavior models.</p>

<p>At last we show that an analyst should be cautious of the
malware bahavior variability when clustering them.</p>

<p>Our findings have important
implications for malware analysts and sandbox operators,
and they emphasize the unique insights that we can gain by
monitoring malware behavior at scale, on real hosts.</p>

<hr />

<p>Any comments of feedback? Email me or comment using Disqus below 😉 (must disable ublock origin to see it)</p>]]></content><author><name>Erin Avllazagaj</name></author><category term="malware" /><category term="malware" /><category term="behavior" /><category term="dynamic analysis" /><category term="variability" /><category term="changed" /><category term="mind" /><category term="real users" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Inside commercial malware sandboxes</title><link href="https://albocoder.github.io/malware/2021/06/01/SandboxStudy.html" rel="alternate" type="text/html" title="Inside commercial malware sandboxes" /><published>2021-06-01T00:00:00+00:00</published><updated>2021-06-01T00:00:00+00:00</updated><id>https://albocoder.github.io/malware/2021/06/01/SandboxStudy</id><content type="html" xml:base="https://albocoder.github.io/malware/2021/06/01/SandboxStudy.html"><![CDATA[<p><img src="https://i.imgur.com/gzO8TuY.jpg" alt="Koro Sensei sandbox" /></p>

<h2 id="introduction">Introduction</h2>

<p>In this blog post I will explore the commercial malware sandboxes. It appears malware is <strong>allowed</strong> to access the internet in many sandboxes <img src="https://cdn.betterttv.net/emote/5e9c6c187e090362f8b0b9e8/1x" alt="KEKW" />. So with that access I decided to collect all the environmental features I could think of and send them to my discord channel. Since Python is the language with the largest library support ever (maybe?) I wrote a python script to take many environment features through <code class="language-plaintext highlighter-rouge">psutil</code>, <code class="language-plaintext highlighter-rouge">platform</code>, <code class="language-plaintext highlighter-rouge">cpuinfo</code> etc. The whole <a href="https://gist.github.com/Albocoder/43827f62dceb0970d4810e3719b993d9"><em>python code</em></a> is here.</p>

<p>Then we use pyinstaller to create a single executable (~10 MB <img src="https://cdn.betterttv.net/emote/5e4e7a1f08b4447d56a92967/1x" alt="PogO" />) using a command like this: <code class="language-plaintext highlighter-rouge">pyinstaller --onefile --noupx sandbox-env-stealer.py</code>. Then for the sake of completeness we compressed the executable in the latest upx and uploaded to VirusTotal. After a few crashes we ended with <em>4</em> samples:</p>

<ul>
  <li><a href="https://www.virustotal.com/gui/file/47ed17bdea1dab10fdee7f61dff8b8f33ad5d092b3e1e5f0f5a3522a27798183/detection">47ed17bdea1dab10fdee…</a></li>
  <li><a href="https://www.virustotal.com/gui/file/07a783fc3ae6a065dc0bfad5e8f89ec3ae5be3bb1ec8ada6f774710399f65305/detection">07a783fc3ae6a065dc0b…</a></li>
  <li><a href="https://www.virustotal.com/gui/file/e472c0493a9a35b7975ca2b5acb4663746993c55c6f2d94742d301d88f050e95/detection">e472c0493a9a35b7975c…</a></li>
  <li><a href="https://www.virustotal.com/gui/file/88d38301327da310c5c00a0b3ae8209730e033f3c57575a447096d94a647e816/detection">88d38301327da310c5c0…</a> (disregard the file name <img src="https://cdn.betterttv.net/emote/56e9f494fff3cc5c35e5287e/1x" alt="monkaS" />)</li>
</ul>

<p>Of course, you can <a href="/assets/misc/sandbox_reports.zip">download</a> the data I collected and play for yourself.</p>

<h2 id="experiment-setup">Experiment setup</h2>

<p>I submitted one of the first samples on <em>17/03/2020</em> (because corona lockdown was getting boring). Then life happened. After deciding to revisit this project I submitted the other 4 samples around March 2021. 
I collected the following features (some ommited for brevity):</p>

<ul>
  <li>Windows version + arch</li>
  <li>CPU name + core count</li>
  <li>local and internet IP address</li>
  <li>CPU counters</li>
  <li>disk partitions and counters</li>
</ul>

<h2 id="what-did-learn">What did learn?</h2>

<h3 id="os-version">OS version</h3>

<p>First, I noticed that <strong>78%</strong> of all the sandboxes run Windows 7 build 7601 (the most famous pirated version <img src="https://cdn.betterttv.net/emote/59b73909b27c823d5b1f6052/1x" alt="" />).
That accounts for 40 out of the 51 executions. The table below illustrates it all. As it seems, the only versions are Windows 7 (build 7600 and 7601) and some flavor of Windows 10.
A malware sample will have a high chance of being run on a real machine if the detected OS is anything but the following.</p>

<table>
  <thead>
    <tr>
      <th>OS version</th>
      <th># execs</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Win 7 build 7601</td>
      <td>40</td>
    </tr>
    <tr>
      <td>Win 10.0.18362</td>
      <td>3</td>
    </tr>
    <tr>
      <td>Win 7 build 7600</td>
      <td>3</td>
    </tr>
    <tr>
      <td>Win 10.0.14393</td>
      <td>3</td>
    </tr>
    <tr>
      <td>Win 10.0.17134</td>
      <td>2</td>
    </tr>
  </tbody>
</table>

<h3 id="timeline-analysis">Timeline analysis</h3>

<p>In this section I will analyze some runtime features of the sandboxes. Here there are some interesting features malware authors can use to quickly identify sandboxes.
There are also some lessons I learned from looking at the executions.</p>

<p>First off, I wanted to see how many executions we were getting per malware and how often. This is particularly important since in <a href="/assets/pdf_files/malw_variability.pdf">our paper</a> we showed that a malware need to run in 3 random environments at least every 3 weeks.</p>

<p>It appears, for the oldest malware sample <strong>10</strong> executions appear on the same day as the submission, <strong>1</strong> execution 1 day later, <strong>3</strong> executions 33 days later and <strong>2</strong> executions 62 days later.
This means that the sample was deemed interesting 2 months from its first “appearance”.
However, we can’t conclude that this is what happens for all the samples, with different number of VT engine detections or with more interest from individual AV vendors.
For the record, I did get an execution report back from the first sample just yesterday, after clicking the “<em>reanalyze</em>” on VT <img src="https://cdn.betterttv.net/emote/5ab6f0ece1d6391b63498774/1x" alt="" />. 
Either way, the malware was executed about 2 to 3 times every month, which is close enough to 3 weeks (that we recommend in our paper), but we demonstrated in the paper that on average 1 week stale of data decreases the detection rate.</p>

<h3 id="environment-analysis">Environment analysis</h3>

<p>This sections will show some environment features that the malware can read.</p>

<h4 id="the-running-processes">The running processes</h4>

<p>A common routine seen on many malware and benign samples is that or iterating the running processes.
I (as in python libraries) use a similar routine to retrieve the running processes.
The table below shows some of the running processes and the number of machines they were seen to run on.
Something interesting we can see here are the “special” programs. In some machines we see the appearance of <code class="language-plaintext highlighter-rouge">bitcoin-qt.exe</code>, <code class="language-plaintext highlighter-rouge">infinium.exe</code> etc, while in some others we see <code class="language-plaintext highlighter-rouge">steam.exe</code>, <code class="language-plaintext highlighter-rouge">SteamService.exe</code> etc, in some others <code class="language-plaintext highlighter-rouge">filezilla.exe</code> or <code class="language-plaintext highlighter-rouge">centralcreditcard.exe</code>.
This is usually done to see if the malware is a crypto miner, a game hack, a file infector or a point-of-sale malware respectively.</p>

<div style="height:300px;overflow:auto;">
<table>
<tr><th>Processes</th><th>Number of machines</th></tr>

<tr><td>...</td><td>...</td></tr>

<tr><td>conhost.exe         </td><td>   47  </td></tr>
<tr><td>lsass.exe           </td><td>   46  </td></tr>
<tr><td>spoolsv.exe         </td><td>   46  </td></tr>
<tr><td>wininit.exe         </td><td>   46  </td></tr>
<tr><td>smss.exe            </td><td>   46  </td></tr>
<tr><td>System Idle Process </td><td>   46  </td></tr>
<tr><td>explorer.exe        </td><td>   46  </td></tr>
<tr><td>winlogon.exe        </td><td>   46  </td></tr>
<tr><td>System              </td><td>   46  </td></tr>
<tr><td>services.exe        </td><td>   46  </td></tr>
<tr><td>opera.exe           </td><td>   43  </td></tr>
<tr><td>firefox.exe         </td><td>   43  </td></tr>
<tr><td>dwm.exe             </td><td>   41  </td></tr>
<tr><td>lsm.exe             </td><td>   40  </td></tr>
<tr><td>Skype.exe           </td><td>   29  </td></tr>
<tr><td>OSPPSVC.EXE         </td><td>   27  </td></tr>
<tr><td>taskeng.exe         </td><td>   24  </td></tr>

<tr><td>...</td><td>...</td></tr>

<tr><td>bitcoin-qt.exe                                                               </td><td>   14 </td></tr>
<tr><td>infium.exe                                                                   </td><td>   14 </td></tr>
<tr><td>qip.exe                                                                      </td><td>   14 </td></tr>
<tr><td>communicator.exe                                                             </td><td>   14 </td></tr>
<tr><td>bitcoind.exe                                                                 </td><td>   14 </td></tr>
<tr><td>steam.exe                                                                    </td><td>   14 </td></tr>
<tr><td>sppsvc.exe                                                                   </td><td>   12 </td></tr>
<tr><td>vslvqrlijtvi.exe                                                             </td><td>   12 </td></tr>
<tr><td>splwow64.exe                                                                 </td><td>   12 </td></tr>
<tr><td>artifact.exe                                                                 </td><td>   10 </td></tr>
<tr><td>fontdrvhost.exe                                                              </td><td>   10 </td></tr>
<tr><td>SteamService.exe                                                             </td><td>    9 </td></tr>
<tr><td>SearchProtocolHost.exe                                                       </td><td>    9 </td></tr>
<tr><td>SearchFilterHost.exe                                                         </td><td>    9 </td></tr>
<tr><td>GoogleUpdate.exe                                                             </td><td>    8 </td></tr>
<tr><td>dllhost.exe                                                                  </td><td>    8 </td></tr>
<tr><td>ioynossujx.exe                                                               </td><td>    8 </td></tr>
<tr><td>wqwupyjrsw.exe                                                               </td><td>    8 </td></tr>
<tr><td>notepad.exe                                                                  </td><td>    8 </td></tr>
<tr><td>taskmgr.exe                                                                  </td><td>    7 </td></tr>
<tr><td>ONENOTEM.EXE                                                                 </td><td>    7 </td></tr>
<tr><td>sihost.exe                                                                   </td><td>    6 </td></tr>
<tr><td>vmtoolsd.exe                                                                 </td><td>    6 </td></tr>
<tr><td>SearchUI.exe                                                                 </td><td>    6 </td></tr>
<tr><td>TrustedInstaller.exe                                                         </td><td>    6 </td></tr>
<tr><td>1a7446534577bab0984f5eb275bdf1f43ed92dfc.exe                                 </td><td>    6 </td></tr>
<tr><td>ivpvkimw.exe                                                                 </td><td>    6 </td></tr>
<tr><td>utg2.exe                                                                     </td><td>    6 </td></tr>
<tr><td>Helios12.exe                                                                 </td><td>    5 </td></tr>
<tr><td>OfficeClickToRun.exe                                                         </td><td>    5 </td></tr>
<tr><td>OmniPOS.exe                                                                  </td><td>    5 </td></tr>
<tr><td>ifs.exe                                                                      </td><td>    5 </td></tr>
<tr><td>EdcSvr.exe                                                                   </td><td>    5 </td></tr>
<tr><td>Registry                                                                     </td><td>    5 </td></tr>
<tr><td>OUTLOOK.EXE                                                                  </td><td>    5 </td></tr>
<tr><td>wmpnetwk.exe                                                                 </td><td>    5 </td></tr>
<tr><td>CentralCreditCard.exe                                                        </td><td>    5 </td></tr>
<tr><td>8lfuaq3.exe                                                                  </td><td>    4 </td></tr>
<tr><td>SophosFileScanner.exe                                                        </td><td>    4 </td></tr>
<tr><td>e5d46536.exe                                                                 </td><td>    4 </td></tr>
<tr><td>SgrmBroker.exe                                                               </td><td>    4 </td></tr>
<tr><td>nvtray.exe                                                                   </td><td>    4 </td></tr>
<tr><td>hmpalert.exe                                                                 </td><td>    4 </td></tr>
<tr><td>350befaf.exe                                                                 </td><td>    4 </td></tr>
<tr><td>1a34b48b.exe                                                                 </td><td>    4 </td></tr>
<tr><td>avp.exe                                                                      </td><td>    3 </td></tr>
<tr><td>SEDService.exe                                                               </td><td>    3 </td></tr>
<tr><td>Tcpview.exe                                                                  </td><td>    3 </td></tr>
<tr><td>mp3tray.exe                                                                  </td><td>    3 </td></tr>
<tr><td>InstallRite.exe                                                              </td><td>    3 </td></tr>
<tr><td>SavService.exe                                                               </td><td>    3 </td></tr>
<tr><td>scap.exe                                                                     </td><td>    3 </td></tr>
<tr><td>mscorsvw.exe                                                                 </td><td>    3 </td></tr>
<tr><td>SAVAdminService.exe                                                          </td><td>    3 </td></tr>
<tr><td>sdrservice.exe                                                               </td><td>    3 </td></tr>
<tr><td>StartMenuExperienceHost.exe                                                  </td><td>    3 </td></tr>
<tr><td>Procmon.exe                                                                  </td><td>    3 </td></tr>
<tr><td>backgroundTaskHost.exe                                                       </td><td>    3 </td></tr>
<tr><td>HttpLog.exe                                                                  </td><td>    3 </td></tr>
<tr><td>ShellExperienceHost.exe                                                      </td><td>    3 </td></tr>
<tr><td>taskhostw.exe                                                                </td><td>    3 </td></tr>
<tr><td>popwack.exe                                                                  </td><td>    3 </td></tr>
<tr><td>msdtc.exe                                                                    </td><td>    3 </td></tr>
<tr><td>sedsvc.exe                                                                   </td><td>    3 </td></tr>
<tr><td>avpui.exe                                                                    </td><td>    3 </td></tr>
<tr><td>procexp64.exe                                                                </td><td>    3 </td></tr>
<tr><td>Procmon64.exe                                                                </td><td>    3 </td></tr>
<tr><td>SophosCleanM64.exe                                                           </td><td>    2 </td></tr>
<tr><td>module-cargo.exe                                                             </td><td>    2 </td></tr>
<tr><td>WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.exe           </td><td>    2 </td></tr>
<tr><td>MemCompression                                                               </td><td>    2 </td></tr>
<tr><td>DS5FEMT81XbOM0LW.exe                                                         </td><td>    2 </td></tr>
<tr><td>SecurityHealthService.exe                                                    </td><td>    2 </td></tr>
<tr><td>88d38301327da310c5c00a0b3ae8209730e033f3c57575a447096d94a647e816.exe         </td><td>    2 </td></tr>
<tr><td>swc_service.exe                                                              </td><td>    2 </td></tr>
<tr><td>KMSAuto Net.exe                                                              </td><td>    2 </td></tr>
<tr><td>3sO7_zsS.exe                                                                 </td><td>    2 </td></tr>
<tr><td>SophosFS.exe                                                                 </td><td>    2 </td></tr>
<tr><td>TiWorker.exe                                                                 </td><td>    2 </td></tr>
<tr><td>s35zi2y.exe                                                                  </td><td>    2 </td></tr>
<tr><td>ld8itap.exe                                                                  </td><td>    2 </td></tr>
<tr><td>SophosNtpService.exe                                                         </td><td>    2 </td></tr>
<tr><td>GoogleUpdateSetup.exe                                                        </td><td>    2 </td></tr>
<tr><td>SSPService.exe                                                               </td><td>    2 </td></tr>
<tr><td>swi_service.exe                                                              </td><td>    2 </td></tr>
<tr><td>pythonw.exe                                                                  </td><td>    2 </td></tr>
<tr><td>WbjiETqs.exe                                                                 </td><td>    2 </td></tr>
<tr><td>ctfmon.exe                                                                   </td><td>    2 </td></tr>
<tr><td>pw.exe                                                                       </td><td>    2 </td></tr>
<tr><td>swi_filter.exe                                                               </td><td>    2 </td></tr>
<tr><td>hltpwzd.exe                                                                  </td><td>    2 </td></tr>
<tr><td>Sophos.Encryption.BitLockerService.exe                                       </td><td>    2 </td></tr>
<tr><td>uniform-98682.exe                                                            </td><td>    2 </td></tr>
<tr><td>8eu3umxnf.exe                                                                </td><td>    2 </td></tr>
<tr><td>SophosIPS.exe                                                                </td><td>    2 </td></tr>
<tr><td>union_rechnung_install_39213.exe                                             </td><td>    2 </td></tr>
<tr><td>userinit.exe                                                                 </td><td>    2 </td></tr>
<tr><td>gzqhbp.exe                                                                   </td><td>    2 </td></tr>
<tr><td>05a62b54.exe                                                                 </td><td>    2 </td></tr>
<tr><td>SophosSafestore64.exe                                                        </td><td>    2 </td></tr>
<tr><td>sdcservice.exe                                                               </td><td>    2 </td></tr>
<tr><td>swi_fc.exe                                                                   </td><td>    2 </td></tr>
<tr><td>3myJvMOn.exe                                                                 </td><td>    2 </td></tr>
<tr><td>WmiApSrv.exe                                                                 </td><td>    2 </td></tr>
<tr><td>cuckoo-47ed17bdea1dab10fdee7f61dff8b8f33ad5d092b3e1e5f0f5a3522a27798183.exe  </td><td>    2 </td></tr>
<tr><td>mtwebooS.exe                                                                 </td><td>    2 </td></tr>
<tr><td>05a62b54e6e32c406f33d22634b03fe8.exe                                         </td><td>    2 </td></tr>
<tr><td>SnrUNWUv.exe                                                                 </td><td>    2 </td></tr>
<tr><td>msiexec.exe                                                                  </td><td>    2 </td></tr>
<tr><td>follow-sneaky-on-twitch.exe                                                  </td><td>    2 </td></tr>
<tr><td>SophosHealth.exe                                                             </td><td>    2 </td></tr>
<tr><td>...</td><td>...</td></tr>
<tr><td>absolutetelnet.exe        </td>   <td> 1 </td>  </tr>                      
<tr><td>gmmeby.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>outlook.exe               </td>   <td> 1 </td>  </tr>                      
<tr><td>isspos.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>qgksae.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>totalcmd.exe              </td>   <td> 1 </td>  </tr>                      
<tr><td>ncftp.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>whatsapp.exe              </td>   <td> 1 </td>  </tr>                      
<tr><td>igfxCUIService.exe        </td>   <td> 1 </td>  </tr>                      
<tr><td>winscp.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>coreftp.exe               </td>   <td> 1 </td>  </tr>                      
<tr><td>barca.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>socbristol.exe            </td>   <td> 1 </td>  </tr>                      
<tr><td>rundll32.exe              </td>   <td> 1 </td>  </tr>                      
<tr><td>accupos.exe               </td>   <td> 1 </td>  </tr>                      
<tr><td>bedrooms-story-avoid.exe  </td>   <td> 1 </td>  </tr>                      
<tr><td>active-charge.exe         </td>   <td> 1 </td>  </tr>                      
<tr><td>fling.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>vrmafl.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>gmailnotifierpro.exe      </td>   <td> 1 </td>  </tr>                      
<tr><td>pidgin.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>diaryrecent.exe           </td>   <td> 1 </td>  </tr>                      
<tr><td>creditservice.exe         </td>   <td> 1 </td>  </tr>                      
<tr><td>operamail.exe             </td>   <td> 1 </td>  </tr>                      
<tr><td>centralcreditcard.exe     </td>   <td> 1 </td>  </tr>                      
<tr><td>unsecapp.exe              </td>   <td> 1 </td>  </tr>                      
<tr><td>AutoKMS.exe               </td>   <td> 1 </td>  </tr>                      
<tr><td>medical reservoir.exe     </td>   <td> 1 </td>  </tr>                      
<tr><td>alftp.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>netsh.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>wspsvc.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>scriptftp.exe             </td>   <td> 1 </td>  </tr>                      
<tr><td>spgagentservice.exe       </td>   <td> 1 </td>  </tr>                      
<tr><td>slwvdq.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>edcsvr.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>Sysmon.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>american.exe              </td>   <td> 1 </td>  </tr>                      
<tr><td>wmi64.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>flashfxp.exe              </td>   <td> 1 </td>  </tr>                      
<tr><td>axdnik.exe                </td>   <td> 1 </td>  </tr>                      
<tr><td>webpagepioneer.exe        </td>   <td> 1 </td>  </tr>                      
<tr><td>skype.exe                 </td>   <td> 1 </td>  </tr>                      
<tr><td>Memory Compression        </td>   <td> 1 </td>  </tr>                      
<tr><td>fpos.exe                  </td>   <td> 1 </td>  </tr>                      
<tr><td>ApplicationFrameHost.exe  </td>   <td> 1 </td>  </tr>                      
<tr><td>filezilla.exe             </td>   <td> 1 </td>  </tr>                      
<tr><td>spcwin.exe                </td>   <td> 1 </td>  </tr>                      
</table>
</div>
<p><br /></p>

<p>Unfortunately, we can also see things like <code class="language-plaintext highlighter-rouge">cuckoo-47ed17bdea1dab10fdee7f61dff8b8f33ad5d092b3e1e5f0f5a3522a27798183.exe</code> or <code class="language-plaintext highlighter-rouge">1a7446534577bab0984f5eb275bdf1f43ed92dfc.exe</code> which is simply the checksum hash of the sample. An attacker can simply compute the popular checksums (the ones on the <code class="language-plaintext highlighter-rouge">details</code> section in VT) and see if its name is any of those and terminate <img src="https://cdn.betterttv.net/emote/5d6096974932b21d9c332904/1x" alt="" />.</p>

<h4 id="machine-names">Machine names</h4>

<p>One thing I wanted to see is the username the malware in the sandbox will run on. For the most part I was underwhelmed. It appears the machine name remains the same across executions, which may be easy for an attacker to just submit bogus “malware” just to harvest all the machine names. In terms of the context I did see that sometimes the sample was executed as <code class="language-plaintext highlighter-rouge">Administrator</code> meaning that some sandboxes give the “malware” admin privilleges. This is done to make sure that malware can execute.</p>

<div style="height:300px;overflow:auto;">
<table>
<tr><th>Username</th><th>Number of executions</th></tr>
<tr><td>art-PC          </td><td> 7 </td></tr>
<tr><td>PC-4a095e27cb   </td><td> 5 </td></tr>
<tr><td>w7sb64-01       </td><td> 3 </td></tr>
<tr><td>w7x64           </td><td> 3 </td></tr>
<tr><td>z97Otih0P4v-PC  </td><td> 2 </td></tr>
<tr><td>AMAZING-AVOCADO </td><td> 2 </td></tr>
<tr><td>mgvwazbfy       </td><td> 1 </td></tr>
<tr><td>WIN-IMCGBF4ZV49 </td><td> 1 </td></tr>
<tr><td>HAPUBWS-PC      </td><td> 1 </td></tr>
<tr><td>DESKTOP-VXO5LFI </td><td> 1 </td></tr>
<tr><td>QGl87k-PC       </td><td> 1 </td></tr>
<tr><td>IOFXBF742797820 </td><td> 1 </td></tr>
<tr><td>WIN-QVM8C8V0B1E </td><td> 1 </td></tr>
<tr><td>XDuwTfOno       </td><td> 1 </td></tr>
<tr><td>dillon          </td><td> 1 </td></tr>
<tr><td>WIN-BROIECEJLD2 </td><td> 1 </td></tr>
<tr><td>CZAC38122213349 </td><td> 1 </td></tr>
<tr><td>Xj8Uz1ljKXdt-PC </td><td> 1 </td></tr>
<tr><td>DESKTOP-ILTLN65 </td><td> 1 </td></tr>
<tr><td>OngJeNyHDSzmoUHw</td><td> 1 </td></tr>
<tr><td>Anna-PC         </td><td> 1 </td></tr>
<tr><td>DcXhlNjDfk-PC   </td><td> 1 </td></tr>
<tr><td>WIN-FYI1QSCHQHU </td><td> 1 </td></tr>
<tr><td>WIN-LQOUJKDIROR </td><td> 1 </td></tr>
<tr><td>XGUW12547433669 </td><td> 1 </td></tr>
<tr><td>AL MUKALLA      </td><td> 1 </td></tr>
<tr><td>WIN-KRAZH63AMC2 </td><td> 1 </td></tr>
<tr><td>PC              </td><td> 1 </td></tr>
<tr><td>GCSPJUXFT667743 </td><td> 1 </td></tr>
<tr><td>DESKTOP-D019GDM </td><td> 1 </td></tr>
<tr><td>WIN-UJ21PNWQMR2 </td><td> 1 </td></tr>
<tr><td>Lisa-PC         </td><td> 1 </td></tr>
<tr><td>vX3juZIWR5Wy-PC </td><td> 1 </td></tr>
<tr><td>WIN-TGCR76AWNUB </td><td> 1 </td></tr>
<tr><td>WIN-U1DY5TBDUI7 </td><td> 1 </td></tr>
</table>
</div>
<p><br /></p>

<h3 id="hardware-analysis">Hardware analysis</h3>

<h4 id="cpu-and-memory-analysis">CPU and memory analysis</h4>

<p>Next we are interested to know what CPU are the sandboxes running on?
I realized the <code class="language-plaintext highlighter-rouge">platform.processor()</code> was not returning the actual CPU name, but it was too late when I realized so I used the available information from this command and scrapped some tables online to get the processor name (<a href="/assets/misc/cpu_name_database.pkl">here</a> is the <em>pkl</em> file that aggregates all data).
In the latest version I added this awesome library called <code class="language-plaintext highlighter-rouge">cpuinfo</code>, so in case you need to collect your own data the cpuinfo will do the work.</p>

<table>
  <thead>
    <tr>
      <th>number of cores</th>
      <th>potential cores</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>4</td>
      <td>{ 16 }</td>
    </tr>
    <tr>
      <td>2</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
    <tr>
      <td>1</td>
      <td>{ 8 , 4 }</td>
    </tr>
  </tbody>
</table>

<p>As hypothesized, the machine has the wrong number of cores for the CPU name (VICTORY <img src="https://cdn.betterttv.net/emote/5f1b0186cf6d2144653d2970/1x" alt="" />). In the <strong>9</strong> machines, I found that the most used CPUs are <code class="language-plaintext highlighter-rouge">Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz</code>, <code class="language-plaintext highlighter-rouge">Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz</code> and <code class="language-plaintext highlighter-rouge">Intel(R) Xeon(R) W-2140B CPU @ 3.20GHz</code>. The machines seem to have 1 core for the most times but the name is that of a cpu with 4 or 8 cores.</p>

<p>I also checked the CPU utilization rates. Hypothetically, the sandboxes would have low CPU utilization, since they are only meant to run the malware.</p>

<p><img src="/assets/blog1_sandbox/cpu_utilization.svg" alt="" /></p>

<p>While most of the sandboxes have a utilization less than 20% there are still cases where sandboxes have 80%  or even 100%. I believe the CPU utilization cannot be a feature to distingush between sandboxes and real machines.</p>

<p>Lastly for this section we look into memory. How much memory can commercial sandboxes spare?</p>

<p><img src="/assets/blog1_sandbox/memory_in_machines.svg" alt="" /></p>

<p>It appears most of the machines have 1 to 2 GB of RAM to spare. In 2 cases I found machines with 512MB of memory like its 2010 <img src="https://cdn.betterttv.net/emote/55cbeb8f8b9c49ef325bf738/1x" alt="" />. Props to the AV vendor(s) that allocated 4, 8 and 16GB for a sandbox to run a malware sample.</p>

<h4 id="disk-partitions">Disk Partitions</h4>

<p>An interesting result here is that in ALL sandboxes there seems to be 1 disk partion of type <code class="language-plaintext highlighter-rouge">cdrom</code> <img src="https://cdn.betterttv.net/emote/58e5abdaf3ef4c75c9c6f0f9/1x" alt="" />.
Hypothetically this may happen because during the VM creation the Windows installation disk was left inserted (more work needs to be done to verify this).</p>

<p>For the main disk partition (where Windows is installed) we notice quite a spectrum of sizes, but the usage in general is quite low (except for the <code class="language-plaintext highlighter-rouge">32GB</code> disks with high usage because Windows is installed there). In 2021 I wouldn’t expect there to be many machines left with 32GB in the main drive, so this may raise some suspicions from the attackers prespective.</p>

<p><img src="/assets/blog1_sandbox/disk_sizes.svg" alt="" />
<img src="/assets/blog1_sandbox/disk_usages.svg" alt="" /></p>

<h4 id="network-interfaces">Network Interfaces</h4>

<p>In general 2 to 3 interfaces and <strong>5</strong> network interfaces in 1 sandbox. The most prevalent are of course <code class="language-plaintext highlighter-rouge">Loopback Pseudo-Interface 1</code> and <code class="language-plaintext highlighter-rouge">Local Area Connection</code>.
Then I noticed some pattern of <code class="language-plaintext highlighter-rouge">isatap.{&lt;random GUID here&gt;}</code> and <code class="language-plaintext highlighter-rouge">Teredo Tunneling Pseudo-Interface</code>. Upon further analysis I found that these interface exist to enable IPv6 communication, so there is nothing special that an attacker can use here <img src="https://cdn.betterttv.net/emote/5d7eefb7c0652668c9e4d394/1x" alt="" />.</p>

<h4 id="battery">Battery</h4>

<p>Not a single sandbox had a battery. Attackers right now pulling a high IQ strat <img src="https://cdn.betterttv.net/emote/5acdc7cb31ca5d147369ead8/1x" alt="" />.</p>

<h3 id="time-analysis">Time analysis</h3>

<p>One thing its hard to keep up to date while restoring the snapshot (at least in Windows) is local time.
Its a bit more difficult when you consider the geolocation the machine is supposed to be at.
For this I collected the local time, the global UTC time and the external IP address of the sandbox and metadata for that IP.
Thanks to <a href="https://www.ipify.org/"><code class="language-plaintext highlighter-rouge">ipfy</code></a>, <a href="https://just-the-time.appspot.com/"><code class="language-plaintext highlighter-rouge">just-the-time</code></a>, and <a href="https://ipinfo.io/"><code class="language-plaintext highlighter-rouge">ipinfo</code></a> for the awesome service.
The process I followed is pretty simple. I use the data from <code class="language-plaintext highlighter-rouge">ipinfo</code> to get the geolocation and convert the UTC time to the local time for that geolocation, then I calculated the time skew between the time of the geolocation and the sandboxes local time.</p>

<table>
  <thead>
    <tr>
      <th>time difference(hours)</th>
      <th>number of machines</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>-19.0</td>
      <td>1</td>
    </tr>
    <tr>
      <td>-9.0</td>
      <td>2</td>
    </tr>
    <tr>
      <td>-8.0</td>
      <td>1</td>
    </tr>
    <tr>
      <td>-3.0</td>
      <td>1</td>
    </tr>
    <tr>
      <td>-2.0</td>
      <td>18</td>
    </tr>
    <tr>
      <td>-1.0</td>
      <td>4</td>
    </tr>
    <tr>
      <td><strong>0.0</strong></td>
      <td><strong>19</strong></td>
    </tr>
    <tr>
      <td>3.0</td>
      <td>1</td>
    </tr>
    <tr>
      <td>5.0</td>
      <td>1</td>
    </tr>
    <tr>
      <td>9.0</td>
      <td>2</td>
    </tr>
  </tbody>
</table>

<p>As it appears, around <strong>65%</strong> of the sandboxes have some sort of a time skew (and I believe the other 35% are simply lucky to VPN on the same timezone or not even VPN at all, will get to this later <img src="https://cdn.betterttv.net/emote/59f27b3f4ebd8047f54dee29/1x" alt="" />).</p>

<p>This is a very low effort feature a real malware can use to check if its inside a sandbox given that such info can be checked for free.
Even through a CnC server the attacker can measure whether the malware has made it into the sandboxes.
Throughout our <a href="/assets/pdf_files/malw_variability.pdf"><code class="language-plaintext highlighter-rouge">paper</code></a> we also noticed that there are some real users’ machines with an outdated clock (sometimes out of date by about 50 years <img src="https://cdn.betterttv.net/emote/5d20a55de1cfde376e532972/1x" alt="" />) however this made up for <em>less than 0.001%</em> of the machines in the real world not 65%, so a malware author may simply choose to forgive these outdated machines just to be safe from analysis.
And from a defender’s prespective, PLEASE UPDATE THE CLOCK BEFORE ROUTING THE NET TRAFFIC TO NARNIA.</p>

<h3 id="ip-analysis">IP analysis</h3>

<p>This is the meat of the blog post, in my opinion. Thanks again to <a href="https://ipinfo.io/"><code class="language-plaintext highlighter-rouge">ipinfo</code></a> for the 7 day free trial. As the most interesting to me, I first looked at “Who owns these IP addresses? Who are the companies/ISPs?”. As shown in the table below the network traffic in all the sandboxes is routed through VPNs that they get from dedicated servers. It appears CrowdStrike has their own IP range that they use to route traffic (registered under their official name <img src="https://cdn.betterttv.net/emote/55f47f507f08be9f0a63ce37/1x" alt="" />, hide the pain Harold).</p>

<div style="height:300px;overflow:auto;">
<table>
<tr><th>IP owner company</th><th>number of IPs in the dataset</th></tr>
<tr><td>TELUS Communications Inc.          </td><td>    5      </td></tr>
<tr><td>Wintek Corporation                 </td><td>    5      </td></tr>
<tr><td>PJSC Vimpelcom                     </td><td>    5      </td></tr>
<tr><td>Verizon Business Special Project   </td><td>    3      </td></tr>
<tr><td>LLC Digital Network                </td><td>    3      </td></tr>
<tr><td>Cox Communications Inc.            </td><td>    3      </td></tr>
<tr><td>Zwiebelfreunde e.V.                </td><td>    2      </td></tr>
<tr><td>Bell Canada                        </td><td>    2      </td></tr>
<tr><td>111250 Russia Moscow SOVINTEL/EDN  </td><td>    2      </td></tr>
<tr><td>1337 Services LLC                  </td><td>    1      </td></tr>
<tr><td>Vodafone D2 GmbH                   </td><td>    1      </td></tr>
<tr><td>Telecom Colocation, LLC            </td><td>    1      </td></tr>
<tr><td>IPG                                </td><td>    1      </td></tr>
<tr><td>111250 Russia MOscow EDN/Sovintel  </td><td>    1      </td></tr>
<tr><td>Bungee Servers SP                  </td><td>    1      </td></tr>
<tr><td><b>CrowdStrike Services</b>        </td><td>  <b>1</b> </td></tr>
<tr><td>Dedicated Servers                  </td><td>    1      </td></tr>
<tr><td>LeaseWeb Netherlands B.V.          </td><td>    1      </td></tr>
<tr><td>ARCOR AG                           </td><td>    1      </td></tr>
<tr><td>Deutsche Telekom AG                </td><td>    1      </td></tr>
<tr><td>Datacamp Limited                   </td><td>    1      </td></tr>
<tr><td>Core-Backbone GmbH                 </td><td>    1      </td></tr>
</table>
</div>
<p><br /></p>

<p>Now we want to know where exatly are these IPs from. With this much data it’s hard to say which IPs are owned by the AV vendors, since they are also just buying VPN access, but I wanted to see where they are buying from. It appears Russia, US, Canada and Germany are the most prominent countries, and in Russia, Moscow seems to be the city with the highest number of IPs.</p>

<p><img src="/assets/blog1_sandbox/countries_piechart.svg" alt="" />
<img src="/assets/blog1_sandbox/IP_locations.svg" alt="" /></p>

<h2 id="conclusion">Conclusion</h2>

<p>There is no silver bullet to detect sandboxes, but there are some features and bugs <img src="https://cdn.betterttv.net/emote/57f540e061ff29ea0eec6f27/1x" alt="" /> the attacker can use to detect them.
On the other hand the sandboxes can also cover these weaknesses. It’s all about that arms race <img src="https://cdn.betterttv.net/emote/5d0d7140ca4f4b50240ff6b4/1x" alt="" />.</p>]]></content><author><name>Erin Avllazagaj</name></author><category term="malware" /><summary type="html"><![CDATA[]]></summary></entry></feed>