 What is going on everybody? Welcome back to another YouTube video. My name is John Hammett and it has been way too long since I have recorded something or posted a video. I had people like asking me like, dude, is everything okay? Are you still alive? It's been like three weeks a month now. So let's do it. Today I want to be talking about how you can recreate StegSolve.jar or at least the good parts of it in Python. I've showcased StegSolve.jar before in tons of videos because it's a pretty common CTF steganography category technique. It'll find the least significant bit or whatever bit you want of whatever color channel in an image and if they or the CTF organizers or the challenge authors have carved an image into that, in those pixels you'd be able to see them and you'd find the flak. I have an example of that from the old MMA Tokyo Westerns, I think 2015 CTF. This is a picture of a castle, right? But in one of the last bits of a color, it'll notify you, hey, this is the flag and it's like written as an image inside of this image. It's not text and the least significant bit like ZSteg would be able to carve out, but it's something you can find in StegSolve.jar. StegSolve.jar though is annoying because it's Java for one thing and it is a GUI application. So you won't be able to easily kind of save all these things automatically and you just want to have a little bit more functionality so you don't have to manually go through and arrow through things, especially if you're trying to work through pixels or images really in bulk. There's no means to go ahead and do that in an automated way. So that's where I want Python to do that work for me and we'll go ahead and showcase how you can do that. So I've got a script we can put together. I'll just call it pysteg.py. I'll bring it down here so you can see it and we will get to work. We're going to need a shebang line, right? The same thing we do with everything. I'm going to use Python 3 because I'm starting to get into the habit of using it. I'm very, very pleased. Obviously, we're going to need the image module from pill image, image, sorry, that should be import. And then let's go ahead and work with an image. Let's do image.open and mine is called castle.png. If you want to pass along like, hey, import sys, use sys argv1, grab a command line argument, you can totally do that. You can make this as extensible as you want, add arguments with argpars.opt, but I'm just going to show you the functionality. I'm just going to show you what we're really doing here. So you've got that imported as an image, and then you work through all of the data you can retrieve with image.load. And you actually want to loop through this, right? So let's do 4x in range of the length of the image.size index.zero because that's going to be our width. And then we'll do the same thing for y, cool. That's going to be index at one, right, to get the height. So now we have that position. So we can just call it pos or position if you want to be, I don't know, it doesn't matter, honestly. You're going to have x and y. And you'll use that to go ahead and grab the color. So we don't even need that line, honestly. We can just say color equals the data represented at x and y. And then we can print out that color. See what it is that we're working with. This is just simple fundamental stuff, right? Let's use Python 3 on our new script, pysteg. And length has no image. That's, or an integer has no length. That's totally fine. I kind of forgot that that is just straight up integer return back to us. I don't know how. But there we go. Now you can see we are looping through all of the color values for r, g and b. And that's the mode of this image. Some images that are PNG or JPEG or whatever they have, they have rgb or sometimes rgba for that alpha channel. As you saw when we opened up StegSolve, that actually looked through the alpha channel as well. If you want to grab these, you totally can. But we can make kind of our script just brute force pull everything. And then it'll be able to detect whether or not it actually has that alpha channel in it or not. So great. We have got the color that we want to work with. We actually can extract out a specific red green or blue. And then we'll get a specific plane or the number of digit that we want in those bits from the byte. Do we want to get the zero bit, the very, very first or last one depending on the direction you're working in or all the way to seven? It's zero based, right? So let's go ahead and make it kind of a general purpose function to do that. I'm going to call mine just get plane. So get plane. And I'm going to pass an argument, which will be the image. And then we also have the channel that we want to look with, look at, which will be rgb or a in that case, and the index, which will be the bit number that we want. So what you can actually do is chest. I'm sorry. Mixing words together. Test if that channel is in the image.mode. You saw the words that I messed up there, channel and test. It's cool. I'm out of practice. I haven't done this for like, what, three weeks? All right. So we're going to end up creating a new image, right? Because we want to be able to extract just like StegSolve did the bit at that color and given the position and slice. So that's going to end up being a black and white picture, kind of the same size and the same properties as the original image. So we need to create a new image with the same size, image dot size as the old one or the one that's passed into this function here. And we need to give it a mode. One will work just fine because that is a black or white image. I actually had an issue with that where I wasn't multiplying it still by 255. I found out that I do need to do that. So we'll see. You'll see black and white colors just fine and you won't screw it up like I did the first time. So new image data will go ahead and equal that new image dot load, which is just as we did earlier when we loaded the original image and we don't particularly need to do that if we end up passing in the data itself. But depending on what you end up doing with this function, maybe you'll change it up. That's fine. I actually will drag the original image data down here. So let's just call that image data. So it'll get the original one and it'll do it each time that it needs to for that function. Then we'll go ahead and grab the channel index. The channel index is going to be grabbing the RG or B string and finding if you passed in channel R, okay it finds it at the zero index. It's the first one there. G will be the first index really when you're zero base with the second index. But anyway it'll get that number and that way we'll be able to index it inside of color just like that. We'll be able to say zero for red or g or one for g, etc. But we can just say channel index inside of our loop and it'll figure it out. The way we can do that is just channel index equals channel dot index or really image dot mode, sorry. And dot mode is actually going to be a string that's returned and you saw that if the channel is in there. So it'll test even if it's an accessible color. Image dot mode, index at the channel. And just checking my notes up here at the top, make sure I didn't forget anything in that regard. But now we can move this loop inside that function. We'll grab the color as we're doing and now we'll go ahead and carve out that channel. The way we can do that is just say color indexed at that channel index, right? That's pretty simple. R, G, B or A, whatever is passed in. And then we'll go ahead and get the specific plane or the bit at the given index for bits that we want. And it's going to be interpreted as binary, right? But we also have to kind of work with that. We need to be able to determine if it's just a 0 or a 1 given the slice that we select whether we want 0, 1, 2, 3, 4, all the way to 7. So let's get the channel which will be that R color or G color or B color. Take it as a binary and that's going to return that 0, B syntax and we'll cut it up. We'll slice off that 0, B with the 2 colon to slice it off. And that has to have a length of 8, right? Because we're going to be indexing from 0 to 7. So I'll use Z fill just to fill that up. And then we can keep moving on. Now we can say I've got the plane here. Let's print that out. Let's just print out this plane, see how it looks. Let's do def call get plane, right? With our image let's get the red channel and let's just get the last bit and the 0 here. Print that out. There we go. Data is not defined because I called that variable image data. You guys totally caught that. You should have told me. Why didn't you yell at me? I'm sure you did. So there are all of our binary values, right? Now let's just cut it up. Let's grab the one that we want. What we can do is try and grab the at the new image data position, X and Y as we're looping through all of it. We can set that color to equal what we're going to end up finding with the plane at that index. So it'll be a 0 or a 1, right? But you have to go ahead and multiply that in this case. You actually also have to invert it if you want to go 0 to 7 the way that Steg solve does. And you can do that by simply saying absolute value of index minus 7. So if you gave it 0 it would consider it 7, which would be the last one. And you kind of just flip flop your direction there, which works just fine. So let's use 255 times the integer of all of that. And that sets that image for us, except if it were to fail on an index error, that's probably what you'll see in case you don't have that index in the range of 0 to 7. If that's the case, I don't care. Don't deal with it and just pass. So now after you have completed looping through all of those colors, all those positions, and with your x and y coordinates, you can just return the new image that you've built. Super easy. And that's that. That's the function. It'll just go ahead and carve out a new image based on that plane given a color. So now we can see what we've got here. We can say new image, or x really, it doesn't matter, can equal get plane image r0 and let's do x show. Let's print this out. And we have just cut it up, sliced at the r or red color, the zero bit there. And just as seg solve did, and if you wanted to, we can loop through this and see all the others because, hey, now we've just automated it in Python. We can use four channel in image.mode, just as we have before to get all the channels RGB. And we can say for plane in range of eight, right, get all the bits for every single color. Let's drag this new image creation function in there. And we'll pass in those variables that we're working with now, channel RGB and plane zero through eight. And let's go ahead and save these so we can take a look at them. I'm just going to use an f string because that's super cool. Let's say channel plane.png. And that's all we need. We save the file and now we can look through all of those images just fine. So what I'm going to do here is run our script, takes a little bit because it's got to loop through all those channels and all those positions for each rendition of the image. But eventually you'll end up with a lot of new files and you can save these or do whatever you want with them. So let's go ahead and grab anything that has a hyphen.png. And you can see b0, b1, b2, b3, b4, etc. If I were to kind of zoom in on all these, you have the exact same functionality that you had with Steg Solve. And you can save those images, you can work with them, you can keep passing them through, or you can generate something that'll show them all at the same time. So you don't forget to arrow through them in Steg Solve, or you can help generate a report, whatever you want to do. If you have more than one image, this is a pretty quick and easy way to do that. And I think it's neat. But again, it's up to you now. If you want to improve the script, maybe add those arguments or little command line stuff, you have that option. This is just the functionality. All it is is looping through it, grabbing that bit at whatever color or position you ask for, and then saving that as a new image. So very, very cool stuff. Hope you guys enjoyed this. I know a lot of people have been asking for some pill and like pipe on image processing. So it sounds like I've got to do more of those videos. Say the word. Leave me a comment, email me, nag me. There's a Discord server. Join the Discord server if you aren't already in there. You can always reach me there. Sometimes it takes me a little time to respond, but I always will at some point. If you like this video, like, comment, subscribe, you know all the good YouTube stuff. Love you guys. See you in the next video.